同时等待多个任务中的一个任务

时间:2018-07-06 00:35:57

标签: c# multithreading async-await task

想象一个初始化任务和两个工作器:

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);
});

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) { }
});

后面的示例仅将“ C完成等待”打印到控制台。为什么会这样?

1 个答案:

答案 0 :(得分:5)

在后台,异步/等待基于任务继续来构建状态机。您可以使用ContinueWith和一些技巧来模仿它(详细信息超出了此答案的范围)。

您需要知道的是,它使用连续性,并且连续性(默认情况下)是一次同步发生。

在这种情况下,B和C都在Init上创建延续。这些延续将继续进行。因此,当C首先继续并进入无限循环时,它将阻止B继续。


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

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

附录

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

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

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