WhenAll()没有按预期工作

时间:2015-11-27 13:21:21

标签: c# .net winforms task-parallel-library begininvoke

我需要让UI线程等到任务数组完成执行。下面代码的问题是 - 任务调用UI线程写入文本框。如何解决这个问题?

public partial class FormConsole : Form
{
    public FormConsole()
    {
        InitializeComponent();
    }
    void txtSayHello_Click(object sender, EventArgs e)
    {
        Class1 objclss = new Class1();
        objclss.formConsole = this;

        Task[] taa = new Task[4];
        taa[0] = new Task(() => objclss.DoSomeThigs("Hello world"));
        taa[1] = new Task(() => objclss.DoSomeThigs("Hello world1"));
        taa[2] = new Task(() => objclss.DoSomeThigs("Hello world2"));
        taa[3] = new Task(() => objclss.DoSomeThigs("Hello world3"));

        foreach(Task task in taa)
        {
            task.Start();
        }
        Task.WhenAll(taa);
        this.txtConsole.AppendText("All threads complete");
    }

    delegate void doStuffDelegate(string value);

    public void doStuff(string value)
    {        
        if (System.Windows.Forms.Form.ActiveForm.InvokeRequired && IsHandleCreated)
        {
            BeginInvoke(new doStuffDelegate(doStuff), value);        
        }
        else
            txtConsole.AppendText(value);        
    }

}

public class Class1
{
    public FormConsole formConsole;
    public void DoSomeThigs(string sampleText)
    {
        formConsole.doStuff(sampleText);
    }
}

现在o / p:Console Redirection TestAll threads completeHello worldHello world1Hello world2Hello world3

我需要:Console Redirection TestHello worldHello world1Hello world2Hello world3All threads complete

解决方案是什么?

3 个答案:

答案 0 :(得分:5)

Task.WhenAll返回一个任务,该任务在传递给它的所有任务完成时完成。您必须await此任务,否则该方法将继续执行。

async void txtSayHello_Click(object sender, EventArgs e)
{
    ...
    await Task.WhenAll(taa);
    ...
}

此方法有阻止版本 - Task.WaitAll。它将阻止当前线程,直到完成所有任务,但阻止UI线程不是一个好主意。

此外,在线程池线程上启动任务的首选方法是使用Task.Run

答案 1 :(得分:4)

Task.WhenAll返回Task,表示可枚举中所有这些任务的完成。您需要等待该任务,因为该方法不会阻止该线程。

txtSayHello_Click转换为async void(只应用于事件处理程序)方法并等待从Task.WhenAll返回的任务:

async void txtSayHello_Click(object sender, EventArgs e)
{
    // ...
    await Task.WhenAll(taa);
    // ...
}

此外,您几乎应该总是避免使用Task构造函数。如果您需要在UI线程上运行任务(取决于您对Task.Factory.StartNew实际执行的操作),则应将TaskScheduler.FromSynchronizationContextFormConsole一起使用;如果您需要Task.Run,则应使用taa[0] = Task.Factory.StartNew(() => objclss.DoSomeThigs("Hello world"), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); 吨。含义:

taa[0] = Task.Run(() => objclss.DoSomeThigs("Hello world"));

或者:

{{1}}

答案 2 :(得分:3)

您最有可能将WhenAll()WaitAll()混淆。 如上所述,您可以将asyncawait一起使用,也可以使用:

A)Task.WhenAll(taa).Wait();

B)Task.WaitAll(taa);

但在你的情况下,这将阻止UI线程。因此,最好将其余代码放入Continuation Task并使用Control.Invoke()调用UI操作:

Task.WhenAll(taa).ContinueWith(t =>
{
   this.Invoke(() => this.txtConsole.AppendText("All threads complete"));
});