[英]How to wait on Async tasks
static void Main(string[] args)
{
Action myAction = async () =>
{
await Task.Delay(5);
Console.WriteLine(Interlocked.Add(ref ExecutionCounter, 1));
};
var actions = new[] { myAction, myAction, myAction };
Task.WaitAll(actions.Select(a => Execute(a)).ToArray()); //This blocks, right?
Console.WriteLine("Done waiting on tasks.");
Console.ReadLine();
}
static int ExecutionCounter = 0;
private static Task Execute(Action a)
{
return Task.Factory.StartNew(async () =>
{
await Task.Delay(5);
a();
});
}
This seems simple enough, but naturally the output always looks like this (the order of the numbers change, of course): 这看起来很简单,但是自然地,输出总是看起来像这样(数字的顺序当然会改变):
Done waiting on tasks.
等待任务完成。
2
2
1
1个
3
3
What am I missing here? 我在这里想念什么? Why doesn't
Task.WaitAll
block like I'm expecting it to? 为什么
Task.WaitAll
不会像我期望的Task.WaitAll
阻塞?
So there are several separate bugs here. 因此,这里有几个单独的错误。
First, for Execute
, you're using StartNew
with an async
lambda. 首先,对于
Execute
,您将StartNew
与async
lambda一起使用。 Since StartNew
doesn't have a Task<Task>
returning overload, like Task.Run
does, you've got a method that returns a Task
indicating when the asynchronous operation has finished starting , not when the asynchronous operation has finished, which means that the Task
returned by Execute
will be completed basically right away, rather than after Delay
finishes or the action you call finishes. 由于
StartNew
没有像Task.Run
这样的Task<Task>
返回过载, Task.Run
您有一个返回Task
的方法,该方法指示异步操作何时完成启动 ,而不是异步操作何时完成,这意味着Execute
返回的Task
将立即完成,而不是在Delay
完成或您调用的操作完成之后。 Additionally, there's simply no reason to use StartNew
or Run
at all when running asynchronous methods, you can just execute them normally and await
them without pushing them to a thread pool thread. 此外,运行异步方法时根本没有理由完全使用
StartNew
或Run
,您可以正常执行它们并await
它们,而无需将它们推送到线程池线程中。
Next, Execute
accepts an Action
, which implies that it's a synchronous method that doesn't compute any value. 接下来,
Execute
接受一个Action
,这意味着它是一个同步方法,不计算任何值。 What you're providing is an asynchronous method, but as the delegate doesn't return a Task
, Execute
can't await
it. 您提供的是一个异步方法,但是由于委托没有返回
Task
,因此Execute
无法await
它。 If you want Execute
to handle asynchronous methods, it needs to accept a delegate that returns a Task
. 如果希望
Execute
处理异步方法,则需要接受返回Task
的委托。
So given all of that Execute
should look like this. 因此,鉴于所有
Execute
应该看起来像这样。
private static async Task Execute(Func<Task> action)
{
await Task.Delay(TimeSpan.FromMilliseconds(5));
await action();
}
Next onto the Main
method. 接下来进入
Main
方法。 As mentioned before Execute
is accepting an Action
when you're trying to provide an async
method. 如前所述,当您尝试提供
async
方法时, Execute
接受Action
。 This means that when the action is run the code will continued executing before your actions have finished. 这意味着在运行操作时,代码将在您的操作完成之前继续执行。 You need to adjust it to using a
Task
returning method. 您需要将其调整为使用
Task
返回方法。
After all of that , your code still has a race condition in it, at a conceptual level, that will prevent you from theoretically getting the results in the right order. 所有这一切后,你的代码仍然有它的竞争条件,在概念层面,这将阻止你获得理论上的结果以正确的顺序。 You're performing 3 different operations in parallel, and as a result of that, they can finish in any order.
您正在并行执行3个不同的操作,因此,它们可以按任何顺序完成。 While you are atomically incrementing the counter, it's possible for one thread to increment the counter, then another to run, increment the counter, print its value, then have the other thread run again and print out the value, given you a possible output of what you have, even after fixing all of the bugs mentioned above.
在原子地递增计数器的同时,有可能一个线程输出一个计数器,然后运行另一个线程,递增计数器,打印其值,然后让另一个线程再次运行并打印出该值,这可能是因为您所拥有的,即使修复了上述所有错误之后也是如此。 To ensure that the values are printed in order, you need to ensure that the increment and the console write are performed atomically .
为了确保按顺序打印值,您需要确保增量和控制台写入是原子执行的 。
Now you can write out your Main
method like so: 现在,您可以像这样写出
Main
方法:
int ExecutionCounter = 0;
object key = new object();
Func<Task> myAction = async () =>
{
await Task.Delay(TimeSpan.FromMilliseconds(5));
lock (key)
{
Console.WriteLine(++ExecutionCounter);
}
};
var actions = new[] { myAction, myAction, myAction };
Task.WaitAll(actions.Select(a => Execute(a)).ToArray()); //This blocks, right?
And yes, as your comment mentions, calling WaitAll
will block, rather than being asynchronous. 是的,正如您的评论所提到的,调用
WaitAll
将阻止,而不是异步。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.