特に 4 つのスレッドの使用に関する質問に答えるために、BackgroundWorker を使用した簡単なスケッチを以下に示します。ここでの考え方は、4 つのタスクをセットアップし、実行中のタスクの数を追跡し、すべてのタスクが完了したら再起動するというものです。volatile と interlocked の説明については、Stack Overflow の質問Volatile vs. Interlocked vs. lockを参照してください。
これにより、求めているもの (4 つのスレッド、レスポンシブ UI) が得られますが、エラー処理は行われず、他の問題が発生する可能性があります。1 つの BackgroundWorker がハングする可能性はありますか (「データ転送」がうまくいかない可能性があります)、その場合、悪い状態になりますか?
public partial class Form1 : Form {
private int workersRunning = 0;
private List<BackgroundWorker> workers = new List<BackgroundWorker>();
public Form1() {
InitializeComponent();
for (int i = 0; i < 4; i++) {
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(this.worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.worker_RunWorkerCompleted);
workers.Add(worker);
}
}
private void button1_Click(object sender, EventArgs e) {
this.StartWork();
}
private void StartWork() {
workers.ForEach(worker => worker.RunWorkerAsync());
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){
Interlocked.Decrement(ref workersRunning);
Console.WriteLine("Worker reported completion from thread id " + e.Result);
if(this.workersRunning == 0) {
Console.WriteLine("All workers are done. Start again");
this.StartWork();
} else {
Console.WriteLine(this.workersRunning + " workers are still running.");
}
}
void worker_DoWork(object sender, DoWorkEventArgs e) {
Interlocked.Increment(ref workersRunning);
int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Doing work on thread #" + threadId);
Thread.Sleep(new Random().Next(2000, 5000));
e.Result = "Work done on thread id " + threadId;
}
}