.Net Framework 2.0 以降では、BackgroundWorker コンポーネントを使用してマルチスレッドアプリケーションを作成することができるようになりました。ここでは、BackgroundWorker を使った簡単なサンプルを掲載しています。
スポンサーリンク
サンプルコードの GUI
サンプルコードの GUI になります。開始ボタンでスレッドを起動し、キャンセルボタンスレッドを停止しています。下図から分かるように BackgroundWorker コンポーネントを設置しています。プログレスバーではスレッドの進捗を表示しています。

サンプルコードのGUI
サンプルソースのポイントや注意点
サンプルソースを確認するポイントを列挙しています。サンプルのソースコードにもコメントを記載していますので、詳細はそちらも参照ください。
- スレッドの実行とパラメータの受け渡し型
- メインスレッドからのスレッドのキャンセル(停止)
- スレッドからメインスレッドへの進捗(完了)およびその他情報の通知とその受け取り方
- スレッドから 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();
}
}
}