.Net Framework 2.0 以降では、BackgroundWorker コンポーネントを使用してマルチスレッドアプリケーションを作成することができるようになりました。ここでは、BackgroundWorker を使った簡単なサンプルを掲載しています。
スポンサーリンク
サンプルコードの GUI
サンプルコードの GUI になります。開始ボタンでスレッドを起動し、キャンセルボタンスレッドを停止しています。下図から分かるように BackgroundWorker コンポーネントを設置しています。プログレスバーではスレッドの進捗を表示しています。
サンプルソースのポイントや注意点
サンプルソースを確認するポイントを列挙しています。サンプルのソースコードにもコメントを記載していますので、詳細はそちらも参照ください。
- スレッドの実行とパラメータの受け渡し型
- メインスレッドからのスレッドのキャンセル(停止)
- スレッドからメインスレッドへの進捗(完了)およびその他情報の通知とその受け取り方
- スレッドから TextBox などのコントロールの直接操作はできない
BackgroundWorker を使ったマルチスレッド
VB.NET と C# でのサンプルソースです。詳細はコメントを参照ください。(長いです・・・)
VB.NET
Imports System.ComponentModel Public Class Form1 ''' フォームロードイベント Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load ' キャンセルボタンは初期状態では無効とする CancelButton.Enabled = False ' スレッドのキャンセルを可能とする BackgroundWorker1.WorkerSupportsCancellation = True ' スレッドからの進捗通知を可能とする BackgroundWorker1.WorkerReportsProgress = True End Sub ''' 開始ボタンクリックイベント Private Sub StartButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles StartButton.Click '------------------------------------------ ' スレッドの多重起動はできないための制御 '------------------------------------------ ' GUIで制御する場合 StartButton.Enabled = False ' スタートボタンを無効にする CancelButton.Enabled = True ' キャンセルボタンを有効にする '' プログラムで制御する場合 'If BackgroundWorker1.IsBusy Then ' MessageBox.Show("すでに実行中です") ' Return 'End If '------------------------------------------ ' スレッドの起動 ' BackgroundWorker1_DoWork メソッドが別スレッドで実行される ' パラメータも渡している(Object型なので、パラメータに制約はない) '------------------------------------------ Dim arg As Integer = 30 BackgroundWorker1.RunWorkerAsync(arg) End Sub ''' キャンセルボタンクリックイベント Private Sub CancelButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles CancelButton.Click ' GUIで制御する場合 StartButton.Enabled = True ' スタートボタンを有効にする CancelButton.Enabled = False ' キャンセルボタンを無効にする '' プログラムで制御する場合 'If Not BackgroundWorker1.IsBusy Then ' MessageBox.Show("スレッドは実行されていません") ' Return 'End If '------------------------------------------ ' スレッドのキャンセル ' 強制的に停止するのではなく、スレッドにキャンセルを通知する ' スレッドキャンセルの決定はスレッドメソッド内で行う。 '------------------------------------------ BackgroundWorker1.CancelAsync() End Sub ''' 別スレッドで動作するメソッド Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) _ Handles BackgroundWorker1.DoWork ' パラメータをObject 型からキャストして取り出す Dim seconds As Integer = CType(e.Argument, Integer) ' パラメータの秒数分スレッドを実行させる For i As Integer = 1 To seconds ' キャンセル通知があればスレッドをキャンセルする If BackgroundWorker1.CancellationPending Then e.Cancel = True '--------------------------------- ' キャンセル時は、e.Resultを設定しても完了通知で受け取れない '--------------------------------- e.Result = "スレッドキャンセル" Return End If '1秒停止 System.Threading.Thread.Sleep(1000) Dim prog As Integer = i * 100 / seconds ' 進捗を計算(単位:%) Dim msg As String = "スレッド開始後、" + i.ToString() + " 秒経過しました。" '------------------------------------------ ' 進捗を通知する ' ・BackgroundWorker1_ProgressChanged がメインスレッドから呼び出される ' ・パラメータ1:進捗(%) Integer型 ' ・パラメータ2:その他あれば Object型 '------------------------------------------ BackgroundWorker1.ReportProgress(prog, msg) Next e.Result = "スレッド完了" End Sub ''' スレッドの進捗通知を受け取る Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, _ ByVal e As System.ComponentModel.ProgressChangedEventArgs) _ Handles BackgroundWorker1.ProgressChanged ' スレッドから通知された、パラメータを受け取る ProgressBar1.Value = e.ProgressPercentage ' パラメータ1 TextBox1.Text = CType(e.UserState, String) ' パラメータ2(Object型なのでキャスト) End Sub ''' スレッドの完了通知を受け取る Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _ ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _ Handles BackgroundWorker1.RunWorkerCompleted 'ボタンの有効無効を設定する StartButton.Enabled = True ' 開始ボタン CancelButton.Enabled = False ' キャンセルボタン If e.Cancelled Then TextBox1.Text = "キャンセルされました" ProgressBar1.Value = 0 '' キャンセル時にResultプロパティから値を取得すると例外が発生する 'TextBox1.Text = e.Result.ToString() Return End If ' スレッドの結果を取得 TextBox1.Text = e.Result.ToString() End Sub End Class
C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace SampleApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } /// フォームロードイベント private void Form1_Load(object sender, EventArgs e) { // キャンセルボタンは初期状態では無効とする cancelButton.Enabled = false; // スレッドのキャンセルを可能とする backgroundWorker1.WorkerSupportsCancellation = true; // スレッドからの進捗通知を可能とする backgroundWorker1.WorkerReportsProgress = true; } /// 開始ボタンクリック private void startButton_Click(object sender, EventArgs e) { //------------------------------------------ // スレッドの多重起動はできないための制御 //------------------------------------------ // GUIで制御する場合 startButton.Enabled = false; // スタートボタンを無効にする cancelButton.Enabled = true; // キャンセルボタンを有効にする //// プログラムで制御する場合 //if (backgroundWorker1.IsBusy) { // MessageBox.Show("すでに実行中です"); // return; //} //------------------------------------------ // スレッドの起動 // backgroundWorker1_DoWork メソッドが別スレッドで実行される // パラメータも渡している(object型なので、パラメータに制約はない) //------------------------------------------ int arg = 30; backgroundWorker1.RunWorkerAsync(arg); } /// キャンセルボタンクリック private void cancelbutton_Click(object sender, EventArgs e) { // GUIで制御する場合 startButton.Enabled = true; // スタートボタンを有効にする cancelButton.Enabled = false; // キャンセルボタンを無効にする //// プログラムで制御する場合 //if (!backgroundWorker1.IsBusy) { // MessageBox.Show("スレッドは実行されていません"); // return; //} //------------------------------------------ // スレッドのキャンセル // 強制的に停止するのではなく、スレッドにキャンセルを通知する // スレッドキャンセルの決定はスレッドメソッド内で行う。 //------------------------------------------ backgroundWorker1.CancelAsync(); } /// 別スレッドで動作するメソッド private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // パラメータを object 型からキャストして取り出す int seconds = (int)(e.Argument); // パラメータの秒数分スレッドを実行させる for (int i = 1; i <= seconds; i++) { // キャンセル通知があればスレッドをキャンセルする if( backgroundWorker1.CancellationPending ) { e.Cancel = true; //--------------------------------- // キャンセル時は、e.Resultを設定しても完了通知で受け取れない //--------------------------------- e.Result = "スレッドキャンセル"; return; } //1秒停止 System.Threading.Thread.Sleep(1000); int prog = (int)(i * 100 / seconds); // 進捗を計算(単位:%) string msg = "スレッド開始後、" + i.ToString() + " 秒経過しました。"; //------------------------------------------ // 進捗を通知する // ・backgroundWorker1_ProgressChanged がメインスレッドから呼び出される // ・パラメータ1:進捗(%) int型 // ・パラメータ2:その他あれば object型 //------------------------------------------ backgroundWorker1.ReportProgress(prog, msg); } e.Result = "スレッド完了"; } /// スレッドの進捗通知を受け取る(メインスレッドで動作する) private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // スレッドから通知された、パラメータを受け取る progressBar1.Value = e.ProgressPercentage; // パラメータ1 textBox1.Text = (string)e.UserState; // パラメータ2(object型なのでキャスト) } /// スレッドの完了通知を受け取る(メインスレッドで動作する) private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //ボタンの有効無効を設定する startButton.Enabled = true; // 開始ボタン cancelButton.Enabled = false; // キャンセルボタン if (e.Cancelled) { textBox1.Text = "キャンセルされました"; progressBar1.Value = 0; //// キャンセル時にResultプロパティから値を取得すると例外が発生する //textBox1.Text = e.Result.ToString() return; } // スレッドの結果を取得 textBox1.Text = e.Result.ToString(); } } }