.NETにおけるマルチスレッド・プログラミングでThreadクラスを使用しましたが、BackgroundWorkerクラスは一種のスレッドクラスで、Threadクラスとは違ってフォームデザインモードで配置できます。UIをスレッドからいじりたい場合は,まず最初にReportProgressの使えるBackgroundWorkerの使用を十分に検討すべきで、コンポーネントが関係ないなら,まずは非同期デリゲートを検討すべきです。非同期デリゲートに関しては,.NET Framework 4.0からSystem.Threading.Tasksとか,Parallel Linqといった選択肢が追加されています。
今回は、BackgroundWorkerクラスを使用してプログレスバーを作成します。
まず、次の三種類のイベントハンドラを作成します。なお「bw」はBackgroundWorkerのインスタンスを示します。
RunWorkerAsync メソッドが呼び出されたときに発生するイベントを処理するハンドラを定義します。
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
ReportProgress メソッドが呼び出されたときに発生するイベントを処理するハンドラを定義します。
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
バックグラウンド処理の完了時(bw_DoWorkの実行が完了したとき)、キャンセル時(bw_DoWorkでキャンセル要求)、バックグラウンド処理によって例外発生時のイベントを処理するハンドラを定義します。
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
実際のプログレスバーのプログラムを次に示します。bw_RunWorkerCompletedでは、バックグラウンド処理の完了か、キャンセル要求かを判断するために、「e.Cancelled」を調べています。また、runボタンが押されたときに、「IsBusy」を調べて BackgroundWorker実行中かを判断します。ビジィーでない場合は、「bw.RunWorkerAsync(100)」によりバックグラウンド処理を開始します。RunWorkerAsyncのパラメータは、ループする回数をバックグランド処理に渡しています。このパラメータで、メソッド自身を渡すこともできます。キャンセルボタンが押されたときに、「IsBusy」を調べて BackgroundWorker実行中かを判断します。ビジィーの場合は、「bw.CancelAsync()」によりバックグラウンド処理を終了させます。bw_DoWorkでbw.CancellationPendingを調べて、trueの場合にキャンセル要求してwhileループbreakします。
BackgroundWorker bw = new BackgroundWorker(); public Form1() { Debug.WriteLine("Form1" + " ThreadID:" + Thread.CurrentThread.ManagedThreadId); InitializeComponent(); progressBar1.Maximum = 100; bw = new BackgroundWorker(); bw.WorkerSupportsCancellation = true; bw.WorkerReportsProgress = true; bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); } void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { Debug.WriteLine("ProgressChanged" + " ThreadID:" + Thread.CurrentThread.ManagedThreadId); this.progressBar1.Value = e.ProgressPercentage; } void bw_DoWork(object sender, DoWorkEventArgs e) { Debug.WriteLine("DoWork" + " ThreadID:" + Thread.CurrentThread.ManagedThreadId); BackgroundWorker bw = sender as BackgroundWorker; int i = 0, count = (int)e.Argument; while (i < count) { Debug.WriteLine("DoWork1" + " ThreadID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(50); bw.ReportProgress(++i); if (bw.CancellationPending) { e.Cancel = true; break; } } } void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Debug.WriteLine("RunWorkerCompleted" + " ThreadID:" + Thread.CurrentThread.ManagedThreadId); string msg = string.Empty; if (e.Cancelled) { msg = "Cancelled"; } else { msg = "Done"; } MessageBox.Show(msg); progressBar1.Value = 0; } private void btnRun_Click(object sender, EventArgs e) { if (!bw.IsBusy) { //バックグラウンド操作の実行を開始します。 bw.RunWorkerAsync(100); } } private void btnCanl_Click(object sender, EventArgs e) { if (bw.IsBusy) { bw.CancelAsync(); } }
作成したプログレスバーのプログラムを実行してスレッド番号を確認します。formのスレッド番号は10でDoWorkのスレッド番号は7となっており、別のスレッドで処理されていることが確認できます。ProgressChangedとRunWorkerCompletedは、formと同じスレッドになっています。実行した結果を次に示します。
Form1 ThreadID:10 DoWork ThreadID:7 DoWork1 ThreadID:7 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 ProgressChanged ThreadID:10 DoWork1 ThreadID:7 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 DoWork1 ThreadID:7 ProgressChanged ThreadID:10 ProgressChanged ThreadID:10 RunWorkerCompleted ThreadID:10