想象一个初始化任务和两个工作器:
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秒后,TaskB
和TaskC
将打印到控制台并按预期继续。
但是,当首先完成的任务在等待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完成等待”打印到控制台。为什么会这样?
答案 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
的其他继续运行。