.NET TIPS

バックグラウンド処理を途中でキャンセルするには?[2.0のみ、C#、VB]

デジタルアドバンテージ 遠藤 孝信
2006/04/14

 「TIPS:時間のかかる処理をバックグラウンドで実行するには?」では、BackgroundWorkerコンポーネントの基本的な利用方法について解説した。本稿では、バックグラウンド(別スレッド)で実行されている、時間のかかる処理(以下、重い処理)のキャンセル方法について解説する。

 なお本稿では、前掲のTIPSのサンプル・アプリケーションに[キャンセル]ボタンを追加しながら、BackgroundWorkerコンポーネントのキャンセル処理の実装について説明する。

WorkerSupportsCancellationプロパティの設定

 まずはサンプル・アプリケーションのフォームに[キャンセル]ボタンを追加する。このボタンはバックグラウンド処理を実行中にのみクリック可能とするため、プロパティ・ウィンドウでEnableプロパティをfalseに設定しておく(Nameプロパティは「buttonCancel」に設定)。

 また、バックグラウンド処理をキャンセル可能にするために、すでに配置済みのBackgroundWorkerコンポーネントに対して、WorkerSupportsCancellationプロパティをtrueに設定しておく必要がある。


[キャンセル]ボタンを追加したWindowsフォーム
バックグラウンド処理をキャンセル可能にするために、BackgroundWorkerコンポーネントのWorkerReportsProgressプロパティをtrueに設定しておく。

BackgroundWorkerコンポーネントのキャンセル処理

 BackgroundWorkerコンポーネントにおけるキャンセル処理の流れは以下のようになる。

  1. BackgroundWorkerコンポーネントのCancelAsyncメソッドを呼び出す。これにより、BackgroundWorkerコンポーネントのCancellationPendingプロパティがtrueに設定される。

  2. 重い処理を行っているDoWorkイベント・ハンドラでは、CancellationPendingプロパティがtrueに設定されていないか(=キャンセルが要求されていないか)を定期的にチェックし、trueになっていれば「e.Cancel」*1をtrueに設定して、DoWorkイベント・ハンドラを抜ける(これによりRunWorkerCompletedイベント・ハンドラが実行される)。

  3. 上記2でe.Cancelをtrueに設定して重い処理をキャンセルした場合、RunWorkerCompletedイベント・ハンドラでは「e.Canceled」*2にtrueが設定される。この場合にはキャンセル時の後処理を(もしあれば)行う。キャンセルされていない場合には、e.Canceledはfalseとなる。

*1 「e」はDoWorkEventArgsクラスのオブジェクト。
*2 「e」はRunWorkerCompletedEventArgsクラスのオブジェクト。
※ いずれのクラスもSystem.ComponentModel名前空間に属する。

キャンセル処理を追加したサンプル・アプリケーション

 以下に、キャンセル処理を追加したサンプル・アプリケーションのソース・コードを示す(Form1.cs/Form1.vbのみ)。変更を行っていないコードはグレーで示している。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BGWorker1 {
  public partial class Form1 : Form {
    public Form1() {
      InitializeComponent();
    }

    // [スタート]ボタンのイベント・ハンドラ
    private void buttonStart_Click(object sender, EventArgs e) {
      buttonStart.Enabled = false;
      buttonCancel.Enabled = true;

      // 時間のかかる処理を別スレッドで開始
      bgWorker.RunWorkerAsync(100);
      // DoWorkイベント発生
    }

    // [キャンセル]ボタンのイベント・ハンドラ
    private void buttonCancel_Click(object sender, EventArgs e) {
      // 時間のかかる処理のキャンセル
      bgWorker.CancelAsync();
    }

    // 時間のかかる処理を行うメソッド
    private void bgWorker_DoWork(object sender, DoWorkEventArgs e) {
      // 別スレッドで実行されるため、このメソッドでは
      // UI(コントロール)を操作してはいけない

      // このメソッドへのパラメータ
      int bgWorkerArg = (int)e.Argument;

      // senderの値はbgWorkerの値と同じ
      BackgroundWorker worker = (BackgroundWorker)sender;

      // 時間のかかる処理
      for (int i = 0; i < bgWorkerArg; i++) {

        // キャンセルされてないか定期的にチェック
        if (worker.CancellationPending) {
          e.Cancel = true;
          return;
        }

        System.Threading.Thread.Sleep(100);

        int percentage = i * 100 / bgWorkerArg; // 進ちょく率
        worker.ReportProgress(percentage);
        // ProgressChangedイベント発生
      }
      // このメソッドからの戻り値
      e.Result = "すべて完了";

      // この後、RunWorkerCompletedイベントが発生
    }

    private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
      // 進ちょく状況の表示
      this.Text = e.ProgressPercentage + "%完了";
      progressBar.Value = e.ProgressPercentage;
    }

    private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
      if (e.Cancelled) {
        MessageBox.Show("キャンセルされました");
        // この場合にはe.Resultにはアクセスできない
      } else {
        // 処理結果の表示
        this.Text = e.Result.ToString();
        MessageBox.Show("正常に完了");
      }

      buttonStart.Enabled = true;
      buttonCancel.Enabled = false;
    }
  }
}
Imports System.ComponentModel

Public Class Form1

  ' [スタート]ボタンのイベント・ハンドラ
  Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles buttonStart.Click
    buttonStart.Enabled = False
    buttonCancel.Enabled = True

    ' 時間のかかる処理を別スレッドで開始
    bgWorker.RunWorkerAsync(100)
    ' DoWorkイベント発生
  End Sub

  ' [キャンセル]ボタンのイベント・ハンドラ

  Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles buttonCancel.Click
    ' 時間のかかる処理のキャンセル
    bgWorker.CancelAsync()
  End Sub

  ' 時間のかかる処理を行うメソッド
  Private Sub bgWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
    ' 別スレッドで実行されるため、このメソッドでは
    ' UI(コントロール)を操作してはいけない

    ' このメソッドへのパラメータ
    Dim bgWorkerArg As Integer = CType(e.Argument, Integer)

    ' senderの値はbgWorkerの値と同じ
    Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)

    ' 時間のかかる処理
    For i As Integer = 1 To bgWorkerArg

      ' キャンセルされてないか定期的にチェック
      If worker.CancellationPending Then
        e.Cancel = True
        Return
      End If

      System.Threading.Thread.Sleep(100)

      Dim percentage As Integer = i * 100 / bgWorkerArg ' 進ちょく率
      worker.ReportProgress(percentage)
      ' ProgressChangedイベント発生
    Next

    ' このメソッドからの戻り値
    e.Result = "すべて完了"

    ' この後、RunWorkerCompletedイベントが発生

  End Sub

  Private Sub bgWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgWorker.ProgressChanged
    ' 進ちょく状況の表示
    Me.Text = e.ProgressPercentage & "%完了"
    progressBar.Value = e.ProgressPercentage
  End Sub

  Private Sub bgWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
    If e.Cancelled Then
      MessageBox.Show("キャンセルされました")
      ' この場合にはe.Resultにはアクセスできない
    Else
      ' 処理結果の表示
      Me.Text = e.Result.ToString()
      MessageBox.Show("正常に完了")
    End If

    buttonStart.Enabled = True
    buttonCancel.Enabled = False
  End Sub
End Class
キャンセル処理を追加したサンプル・プログラム(上:Form1.cs、下:Form1.vb)

 CancelAsyncメソッドを呼び出しても、あるいはDoWorkイベント・ハンドラで「e.Cancel」をtrueに設定しても、重い処理が自動的に中断されるわけではない点に少し注意が必要だ。

 キャンセルを行った場合、RunWorkerCompletedイベント・ハンドラでは、処理結果を示す「e.Result」の値にはアクセスできない点にも注意してほしい(アクセスすると例外が発生する)。End of Article

利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:Windowsフォーム 処理対象:スレッド
使用ライブラリ:BackgroundWorkerコンポーネント(System.ComponentModel名前空間)
使用ライブラリ:DoWorkEventArgsクラス(System.ComponentModel名前空間)
使用ライブラリ:ProgressChangedEventArgsクラス(System.ComponentModel名前空間)
使用ライブラリ:RunWorkerCompletedEventArgsクラス(System.ComponentModel名前空間)
関連TIPS:時間のかかる処理をバックグラウンドで実行するには?

この記事と関連性の高い別の.NET TIPS
時間のかかる処理をバックグラウンドで実行するには?
WPF/Windowsフォーム:時間のかかる処理をバックグラウンドで実行するには?(async/await編)[C#/VB]
[ASP.NET]GridViewコントロールの削除ボタンで確認メッセージを表示するには?
Windowsアプリケーション終了時に確認ダイアログを表示するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間