我正在使用Visual Studio 2010 开发.NET Windows Forms 应用程序。
在这个应用程序中,我需要四个后台线程来进行底层数据传输。当所有四个线程都完成后,将再次使用四个线程开始另一轮四个底层数据传输。
表单 UI 需要始终响应。我的问题是:如何控制四个线程的运行?比如:我怎么知道所有线程都完成了?使用不稳定的全局计数器?
我正在使用Visual Studio 2010 开发.NET Windows Forms 应用程序。
在这个应用程序中,我需要四个后台线程来进行底层数据传输。当所有四个线程都完成后,将再次使用四个线程开始另一轮四个底层数据传输。
表单 UI 需要始终响应。我的问题是:如何控制四个线程的运行?比如:我怎么知道所有线程都完成了?使用不稳定的全局计数器?
.NET 4 友好地提供了类Task和相关的统一高级异步 API。因此,您可以安全地从基于 BackgroundWorker 的设计切换到基于 Task 的设计,然后您会看到等到所有 Task 都使用Task.Factory.ContinueWhenAll
.
为了回答你关于使用四个线程的问题,下面是一个使用 BackgroundWorker 的速写。这里的想法是设置四个任务,跟踪正在运行的任务的数量,并在它们全部完成时重新启动。有关 volatile 与 interlocked 的讨论,请参阅 Stack Overflow 问题Volatile vs. Interlocked vs. lock。
这将为您提供您所要求的(四个线程,响应式 UI),但没有错误处理,并且可能存在其他问题。有没有可能一个 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;
}
}