等待多个任务同时执行任务

Ben*_*nni 1 c# multithreading task async-await

想象一下初始化任务和两个工人:

var Init = Task.Run(async () =>
{
    await Task.Delay(1000);
});
var TaskB = Task.Run(async () =>
{
    await Init;
    Console.WriteLine("B finished waiting");
    await Task.Delay(10000000);
});
var TaskC = Task.Run(async () =>
{
    await Init;
    Console.WriteLine("C finished waiting");
    await Task.Delay(10000000);
});
Run Code Online (Sandbox Code Playgroud)

1秒后,,TaskBTaskC打印到控制台,并继续按预期.

但是,当等待Init之后首先完成的任务不使用异步方法时,其他任务await Init调用不会完成:

var Init = Task.Run(async () =>
{
    await Task.Delay(1000);
});
var TaskB = Task.Run(async () =>
{
    await Init;
    Console.WriteLine("B finished waiting");
    await Task.Delay(5000);
});
var TaskC = Task.Run(async () =>
{
    await Init;
    Console.WriteLine("C finished waiting");
    while (true) { }
});
Run Code Online (Sandbox Code Playgroud)

后一个示例仅向控制台打印"C finished waiting".为什么会这样?

The*_*aot 5

在幕后,async/await基于任务延续构建状态机.你可以使用它来模仿它ContinueWith和一点聪明才智(细节超出了这个答案的范围).

你需要知道的是它使用了continuation,并且默认情况下,continuation同步发生,一次一个.

在这种情况下,B和C都在创建延续Init.这些延续将在另一个之后运行.因此,当C首先继续并进入无限循环时,它会阻止B继续.


解决方案?好吧,除了避免无限循环之外,您可以使用具有异步延续的任务.在上面执行此操作的简单方法Task.Run如下:

var Init = Task.Run(async () =>
{
    await Task.Delay(1000);
}).ContinueWith(_ => { }, TaskContinuationOptions.RunContinuationsAsynchronously);
Run Code Online (Sandbox Code Playgroud)

附录

根据OP注释,无限循环表示阻塞调用.我们可以通过不同的方法解决这种情况:

var TaskC = Task.Run(async () =>
{
    await Init;
    Console.WriteLine("C finished waiting");
    await Task.Run(() => {while (true) { }});
});
Run Code Online (Sandbox Code Playgroud)

这样,阻塞调用不是int的延续Init(相反,它将继续延续),因此它不会阻止其他延续Init运行.