BackgroundWorker の使い方 [.NET Framework]
注: このテクノロジは .NET Framework 2.0 以降で利用可能です
イベントハンドラに直接時間のかかる処理を書くと、UI が、いわゆる「固まった」状態になります。これを避けるために、 時間のかかる処理を実行する場合は、UI を制御するスレッドとは別の作業スレッド (Worker Thread) でそれを実行します。 UI を持つプログラムの場合は、通常ユーザーにその作業の進捗状況を表示する必要があるため、UI と、何らかのやり取りが発生します。
BackgroundWorker を利用するには以下を行います:
- DoWork イベントハンドラの設定。これは時間のかかる処理はここから呼び出します。
- 処理を実行するときは、RunWorkerAsync メソッドを呼び出します
- 処理の進捗を UI スレッドで受け取るときには、ProgressChanged イベントを処理します。
一方作業スレッドでは、ReportProgress メソッドを呼び出します。 - 処理の完了は RunWorkerCompleted イベントが発生することでわかります
その他、オプショナルの処理としては次のようなものがあります:
- 処理のキャンセル - UI スレッドでは CancelAsync メソッドを呼び出し BackgroundWorker オブジェクトに処理のキャンセルをリクエストします。作業スレッドでは、時々CancellationPending プロパティをチェックして、処理を中断するかどうか判断します。
- 作業スレッドにパラメータを渡す場合には、(処理を開始する) RunWorkerAsync メソッドにパラメータを渡します。 渡されたパラメータは DoWork イベントハンドラに渡される DoWorkEventArgs の Argument プロパティに渡されます。
簡単なテストコードは次のとおり。
サンプルコード (Visual Studio 2005 C# Windows Application Project)
作業スレッドでは、Sleep を使って処理を遅延させています。処理の開始時にはドロップダウンリストからパラメータを取得し、
かつ、キャンセルボタンでその処理を中断可能としています。
namespace backgroundworder_test { using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; ///////////////////////////////////////////////////////////////////////// public partial class Form1 : Form { public Form1() { InitializeComponent(); } ///////////////////////////////////////////////////////////////////// private void Form1_Load( object sender, EventArgs e ) { // // イベントハンドラの設定 // backgroundWorker1.DoWork += new DoWorkEventHandler( backgroundWorker1_DoWork ); backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler( backgroundWorker1_ProgressChanged ); backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler( backgroundWorker1_RunWorkerCompleted ); // // コントロールの初期化 // comboBox1.SelectedIndex = 1; } ///////////////////////////////////////////////////////////////////// void backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) { // // ボタンの設定 // btnDoWork.Enabled = true; btnCancel.Enabled = false; // // エラーまたはキャンセルのチェック // if (e.Error != null) { MessageBox.Show( e.Error.Message ); return; } else if (e.Cancelled) { MessageBox.Show( "Operation has been cancelled." ); return; } // // 処理完了 // WORK_RESULT res = e.Result as WORK_RESULT; if (res == null) { throw new Exception( "Invalid Context" ); } MessageBox.Show( string.Format( "Work Completed. {0} - {1}", res.value, res.msg ) ); } ///////////////////////////////////////////////////////////////////// void backgroundWorker1_ProgressChanged( object sender, ProgressChangedEventArgs e ) { progressBar1.Value = e.ProgressPercentage; } ///////////////////////////////////////////////////////////////////// private void backgroundWorker1_DoWork( object sender, DoWorkEventArgs args ) { // パラメータの取得 int nMillSec = (int) args.Argument; // 戻り値の準備 WORK_RESULT res = new WORK_RESULT(); // ワーカーオブジェクトの取得 BackgroundWorker worker = sender as BackgroundWorker; if (worker == null) { throw new Exception( "Invalid Context" ); } // 時間のかかる処理・・・ for (int i = 0; i < 20; i++) { // キャンセルのチェック if (worker.CancellationPending) { args.Cancel = true; return; } // Sleep による遅延 System.Threading.Thread.Sleep( 1 * nMillSec ); worker.ReportProgress( (i + 1) * 5 ); } // 結果の設定 res.value = 20; res.msg = "Completed"; args.Result = res; } ///////////////////////////////////////////////////////////////////// private void button1_Click( object sender, EventArgs e ) { btnDoWork.Enabled = false; btnCancel.Enabled = true; int nParam = int.Parse( comboBox1.Text ); progressBar1.Value = 0; backgroundWorker1.RunWorkerAsync( nParam ); } ///////////////////////////////////////////////////////////////////// private void button2_Click( object sender, EventArgs e ) { if (DialogResult.Yes == MessageBox.Show( "Are you sure you want to cancel this operation?", "Confirmation", MessageBoxButtons.YesNo )) { backgroundWorker1.CancelAsync(); } } } // // WORK RESULT // public class WORK_RESULT { public int value; public string msg; } }