.NET Windows Forms应用程序的多线程控制

时间:2012-10-10 04:27:04

标签: .net winforms multithreading

我正在使用Visual Studio 2010开发.NET Windows窗体应用程序。

在这个应用程序中,我需要四个后台线程来进行底层数据传输。当所有四个线程完成后,将再次使用这四个线程启动另一轮四个底层数据传输。

表单UI需要始终响应。我的问题是:如何控制四个线程的运行?喜欢:我如何知道所有线程都已完成?使用易变的全局计数器?

2 个答案:

答案 0 :(得分:2)

.NET 4提供了类 Task ,并且与统一高级别的异步API相关。因此,您可以安全地从基于BackgroundWorker的设计切换到基于任务,然后您将看到等待所有任务以Task.Factory.ContinueWhenAll结束是多么容易。

请参阅 Async Tasks - Simplify Asynchronous Programming with Tasks

答案 1 :(得分:1)

要回答有关使用特定四个线程的问题,下面是使用BackgroundWorker的快速草图。这里的想法是设置四个任务,跟踪正在运行的任务的数量,以及何时完成重启。有关volatile vs 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;
    }
}