我正在试图弄清楚如何使用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");
}
答案 0 :(得分:22)
你造成了僵局。
UI线程正在等待完成4个任务。
另一方面,运行calcSim
的4个任务正在尝试调用UI线程上的代码 - &gt;死锁。
您应该使用Task.WhenAll()
代替。该方法将返回一个新任务,当您完成所有任务后,该任务将标记为已完成。如果您等待该任务,您的UI线程将被释放,因此calcSim
将能够调用UI线程上的代码,从而避免死锁。
<强>更新强>
你错了。你还在使用WaitAll
,这是一个阻止通话。您应该使用WhenAll
替换。
await Task.WhenAll(t);
创建一个任务,该任务将在所有提供的任务完成时完成 完成。
通过在结果上调用await
,您的UI线程将是免费的 - 直到所有4个任务完成。发生这种情况时,您的RunAsync3
方法将会恢复。
答案 1 :(得分:1)
Task.WaitAll
阻止并等待所有任务完成,并在UI线程上调用它。
您的所有任务都试图调用richTextBox1.Invoke
(在UI线程中),但您的UI线程在Task.WaitAll
中被阻止。死锁。
答案 2 :(得分:-1)
因为它会在你的线程结束时等待。他们正好运行3秒300X10