Loading [MathJax]/extensions/tex2jax.js

2013-09-27

C#:BackGroundWorkerコンポーネントを利用したマルチスレッド処理

C#には、マルチスレッドを実現する方法として、BackGroundWorkerというコンポーネントが用意されています。このコンポーネントをフォームに貼り付けることで、容易にマルチスレッドなプログラムを実現できます。以下、サンプルコードを載せますが、ポイントを挙げておきます。

1.フォームにBackGroundWorkerを貼り付ける
タイマーコントロールと同じような感覚で貼りつけてください。1つのコントロールで1スレッドです。

2.開始と終了
  • 開始 ・・・ RunWorkerAsync()
  • 終了 ・・・ CancelAsync();
のメソッドを呼ぶだけです。

3.スレッド関数
コンポーネント変数名_DoWorkで終わる関数が実行されます。CancellationPendingの値を条件としたスレッドループを記述するのがベターなやり方でしょう。

4.途中と終了の処理
_RunWorkerCompletedと_ProgressChangedというメソッドが用意されていて、それぞれ終了時と途中の任意のタイミングで呼び出されます。呼び出しは明示的にしないとダメです。ReportProgressメソッドは引数がint型で進行状態を表す数値を渡すことが正しい使い方です(下記サンプルは違います)。なお、これらのメソッドを使えるようにするには、これらの処理を有効(true)にしておく必要があります。デフォルトはfalseなので注意して下さい。

InvokerとDelegate
マルチスレッドプログラムにおいては、プロセス間のデータの授受が厄介な問題となります。BackGroundWorkerを利用する方法でも、この問題を避ける事ができません。具体的に言うと、異なるプロセス間の変数には直接アクセスすることができないということです。下記の例では、別スレッドでカウントしている値をラベルコンポーネントに表示する際に、直接ラベルに値を代入できないということです。メソッドを用意してそれをDelegateに渡し、Delegateを介してInvoke(起動)する必要があります。(もっとよい方法が見つかりましたので、くわしくは続編を!

public partial class sampleBackgroundWorker : Form {
int i = 0;
//Delegate を宣言しておく
delegate void MyDelegate(int i);
public Form1() {
InitializeComponent();
//以下を有効にしておく
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
//スレッド起動
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
//スレッドが停止状態でなければ
while (!backgroundWorker1.CancellationPending) {
//この呼出でProgressChangedが発生する
backgroundWorker1.ReportProgress(i);
//スレッド内からデリゲートを介して以下のようにして呼ぶ
Invoke(new MyDelegate(count), i);
i++;
}
}
//スレッドが終了した際の処理
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
MessageBox.Show("OK");
}
//進行中の処理
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
if (i == 5000) {
backgroundWorker1.CancelAsync();
}
}
//カウントしていくメソッド
internal void count(int i) {
this.label1.Text=i.ToString();
}
}


0 件のコメント:

コメントを投稿