【问题标题】:Wait for a task in multiple tasks at the same time在多个任务中同时等待一个任务
【发布时间】:2018-07-06 00:35:56
【问题描述】:

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

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 完成等待”打印到控制台。为什么会这样?

【问题讨论】:

标签: c# multithreading async-await task


【解决方案1】:

在幕后,async/await 构建了一个基于任务延续的状态机。您可以使用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 的其他延续运行。

【讨论】:

  • 有趣!不幸的是,一位工作人员在初始化后立即调用了一个阻塞库函数,但您的 ContinueWith 提示真的很有帮助!
  • @Benni 我已经扩展了答案。如果该阻塞调用没有异步 API,请考虑我在此处介绍的方法。
  • 谢谢!但是,它是否优于使用 ContinueWith?
  • @Benni 并行运行 continuation 需要更加小心,以确保同时运行的两个 continuation 不会相互踩到对方脚趾(引入竞争条件),如果您不自在地说它不会不会发生,我建议不要使用它。在这方面,同步运行它们更安全,因此,它是默认值是有道理的……事实上,它曾经是唯一的选择。 RunContinuationsAsynchronously 将让您利用并行性 - 这可能会或可能不会产生更好的性能,请记住,这通常意味着使用线程池中的更多线程。
猜你喜欢
  • 2016-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-02
  • 1970-01-01
  • 2012-07-23
相关资源
最近更新 更多