在调用.WaitAll()C#之后,将匿名任务添加到List <task>不会执行该任务

时间:2018-06-25 17:36:27

标签: c# .net asynchronous task

我通过以下方式将任务添加到任务列表中

taskList.Add(new Task(async () =>
    {
        // work here
        await MethodInsideThatNeedsAwaiting(); // if that has anything to do with it
        // more work 
    }));

此后调用Task.WaitAll(tasklist);会“卡住”。该程序继续运行,但是该列表中的任何任务都不再有任何消息,也没有遇到任何断点,就好像它陷入了自己的内部异步循环中一样。

将任务添加到列表的方式是否错误?这是什么问题?

我也尝试过:
我也尝试了以下方法,如果由于某种原因async关键字是问题所在,但它仍然 无效

taskList.Add(new Task(() =>
    {
        // work here
        MethodInsideThatNeedsAwaiting().Wait();
        // more work 
    }));

但是 可以按预期

private async Task GetStuff()
{
    // same work here
    await MethodInsideThatNeedsAwaiting();
    // more work
}

然后将其添加到taskList.Add(GetStuff());中,调用Task.WaitAll(tasklist);对此没有任何问题。

1 个答案:

答案 0 :(得分:11)

您在这里所做的一切都是错误的。 在编写更多异步代码之前,请停止正在做的事情,并了解其工作原理。您正在为无法完成的任务(因为它们从未开始执行)以及无法完成的任务(正在等待它们)进行设置对自己。异步工作流有些棘手。

首先,您几乎永远不想使用new Task。它只是意味着“做一个代表这项工作的任务”。 这并不意味着要执行该工作new Task列出待办事项;它没有做清单上的东西!

第二,您几乎永远不想使用Task.Run。这意味着要做一个代表工作的任务,并从池中分配一个工人来运行它。。除非您正在执行的工作是同步 CPU绑定,而您没有工作,否则您就不想分配工作线程。

第三,您几乎永远都不想在已经已完成任务的某项上使用。您手中有一个异步lambda。 调用时它返回一个任务,因此,如果您想要一个正在运行的工作流的任务,请调用lambda!

第四,您几乎永远不想WaitAll通过将异步工作流变回同步工作流来破坏整个异步点

第五,出于相同的原因,您几乎永远不想在任务上调用Wait。但情况变得更糟!这是我希望您执行的任务清单:首先,将面包放入烤面包机中,然后开始烘烤。其次,同步等待三明治完成;在完成此步骤之前,请勿继续前进。第三,吃三明治。第四,当烤面包机弹出时,将烤面包机从烤面包机中取出,并在烤面包机上放一些火腿做一个三明治。 如果您尝试执行此工作流程,将永远等待。异步工作流死锁,当您在其中插入同步等待时,因为您经常处于等待自己将来要做的工作的情况中。 >

(除了最后一点:如果您处在这种情况下,正确的解决方案是 NOT 将工作流程的第二步更改为“雇用工人同步完成我的三明治”等待该工作人员完成。”您经常会看到这种奇怪的,浪费的解决方法,用于错误的工作流程。当您删除同步等待并在工作流程无法继续的地方插入异步等待(await)时直到完成一项任务,然后您就会发现您的工作流可以全部由一个线程完成。)

您所做的每一件事绝对都是错误的异步编程方式,如果您继续这样做下去就不会成功

好的,现在您知道了如何不做,如何做?

  • 如果您有返回任务的方法或lambda,请调用它以获取任务
  • 如果您需要等待任务完成才能继续工作流,请await完成该任务。
  • 如果您有多个任务,并且需要全部完成才能继续工作流,请将它们放入集合中,然后调用WhenAll以取回代表完成所有任务的新任务这些任务。然后,您必须await 那个任务。

一些正确的工作流程:

这是最简单的一个:

private async Task GetStuffAsync()
{
    // same work here
    await MethodInsideThatNeedsAwaitingAsync();
    // more work
}
private async Task DoItAsync()
{
    // do work
    await GetStuffAsync();
    // do more work
}

如果您有多个任务并且想要等待所有任务,但又不必彼此等待怎么办?

private async Task DoItAsync()
{
    // do work
    Task t1 = GetStuffAsync();
    // do work
    Task t2 = GetOtherStuffAsync();
    // do more work
    // We cannot continue until both are done
    await t1;
    await t2;
    // do even more work
}

如果您有 个这样的任务,该怎么办?

private async Task DoItAsync()
{
    // do work
    var tasks = new List<Task>();
    while (whatever)
      tasks.Add(GetStuffAsync()); 
    // do work
    // we cannot continue until all tasks are done
    await Task.WhenAll(tasks);
    // do more work
}