8

我试图弄清楚如何使用 Task 类。过去我一直使用常规的 Thread 类,但我正在尝试掌握所有异步编程......

例如,我创建了一个包含所有代码的主 Winforms 应用程序。我的问题的相关代码是:

//Relevant delegates
public delegate void MethodAction(int num);
public delegate void MethodConversion();
public delegate void OnCompletionAction(string completiontext);

//Button user presses
private void button4_Click(object sender, EventArgs e)
{
    richTextBox1.Clear();
    sw.Reset();
    sw.Start();
    Sync.RunAsync3(calcSim);
}

//The method that simulates a calculation by adding a sleep
//the input param threadlength is just to allow threads to take longer than others
//since I'm multithreading, I have to invoke the writing code on the windows RichTextbox control
private void calcSim(int threadlength)
{
    string threadname = Thread.CurrentThread.Name;
    for (int i = 0; i < 10; i++) //Thread calc should take 3s
    {
        Thread.Sleep(300 + threadlength);
        richTextBox1.Invoke((MethodConversion)(() =>
                            { 
                                richTextBox1.AppendText(string.Format("Thread: {0}\tVersion: {1}\n", threadname, (i + 1).ToString())); 
                            }));
    }
}

//Class that contains the different processing methods
public static class Sync
{
    public static event OnCompletionAction OnProcCompletion;

    public static void RunAsync3(MethodAction doM)
    {
        Task[] t = new Task[4];
        for(int i = 0; i < 4; i++)
        {
            t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
        }
        Task.WaitAll(t);
        if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
    }
}

问题出在 Task.WaitAll(t)... 出于某种原因,我无法弄清楚,它完全阻塞在那条线上并且不再响应。如果我省略该行,表单会实时更新,执行大约需要 3 秒。

我的问题是:为什么 Task.WaitAll() 在释放 UI 线程并允许其余代码执行之前不阻塞 3 秒?

我知道它应该阻塞 UI 一段时间(直到计算所有线程),但它会无休止地阻塞整个应用程序。似乎永远在等待?

编辑

有人建议我使用WhenAll 而不是WaitAll。我重写了 RunAsync3 如下:

public static void RunAsync3(MethodAction doM)
{
    Task[] t = new Task[4];
    for(int i = 0; i < 4; i++)
    {
        t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
    }
    //Task.WaitAll(t); -> deadlock
    Task.WaitAll(new Task [] { Task.WhenAll(t) });
    if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}

但这仍然陷入僵局......?我可能错误地使用了WhenAll?

编辑 2

因为每个声称我阻塞 UI 线程的人都是对的,所以我决定尝试另一种方法:通过在 UI 线程中运行一个新线程作为我的调用线程(这样现在阻塞将发生在我的线程而不是 UI 线程上)。这可行,但显然不是最好的方法!

private void button4_Click(object sender, EventArgs e)
{
    Thread t = new Thread(new ThreadStart(() =>
    {
        richTextBox1.Invoke((MethodConversion)(() => richTextBox1.Clear()));
        sw.Reset();
        sw.Start();
        Sync.RunAsync3(calcSim);
    }));
    t.Start();
}
public static void RunAsync3(MethodAction doM)
{
    Task[] t = new Task[4];
    for(int i = 0; i < 4; i++)
    {
        t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
    }
    Task.WaitAll(t);
   //Task.WaitAll(new Task [] { Task.WhenAll(t) });
   if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}
4

3 回答 3

27

你造成了僵局。

UI 线程正在等待完成 4 个任务。

另一方面,这 4 个正在运行的任务calcSim正在尝试调用 UI 线程上的代码 -> 死锁。

你应该Task.WhenAll()改用。该方法将返回一个新任务,当您的所有 for 任务完成后,该任务将被标记为已完成。如果您等待该任务,您的 UI 线程将被释放,因此calcSim将能够调用 UI 线程上的代码,从而避免死锁。

更新

你用错了。您仍在使用WaitAll,这是一个阻塞调用。您应该将其替换WhenAll为.

await Task.WhenAll(t);

文档中:

创建一个将在所有提供的任务都完成后完成的任务。

通过调用await结果,您的 UI 线程将是空闲的 - 直到所有 4 个任务完成。发生这种情况时,您的RunAsync3方法将恢复。

于 2013-11-04T11:19:50.250 回答
1

Task.WaitAll阻塞并等待所有任务完成,您在 UI 线程上调用它。

您的所有任务都在尝试调用richTextBox1.Invoke(在 UI 线程中),但您的 UI 线程在Task.WaitAll. 僵局。

于 2013-11-04T11:18:36.067 回答
-1

因为它会等待您的线程完成。他们运行 3 秒 300X10

于 2013-11-04T11:16:06.890 回答