等待vs任务。等等-僵局?

我不太明白Task.Waitawait之间的区别。

我有类似于以下功能在一个ASP。NET WebAPI服务:

public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}


public async static Task<string> Bar()
{
return await Foo();
}


public async static Task<string> Ros()
{
return await Bar();
}


// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());


return new string[] { "value1", "value2" }; // This will never execute
}
}

Get将死锁。

是什么导致了这种情况?为什么当我使用阻塞等待而不是await Task.Delay时,这不会引起问题?

235627 次浏览

Waitawait虽然在概念上相似,但实际上完全不同。

Wait将同步阻塞直到任务完成。因此,当前线程被阻塞,等待任务完成。作为一般规则,你应该使用“async一直到下”;也就是说,不要阻塞async代码。在我的博客上,我详细介绍了异步代码中的阻塞如何导致死锁

await将异步等待直到任务完成。这意味着当前方法是“暂停的”(它的状态被捕获),并且该方法返回一个未完成的任务给它的调用者。之后,当await表达式完成时,该方法的剩余部分被安排为一个延续。

你还提到了一个“合作块”,我想你的意思是你正在Waiting的任务可能会在等待线程上执行。有些情况下会发生这种情况,但这是一种优化。有很多情况会发生不能,比如任务是另一个调度器的任务,或者它已经启动,或者它是非代码任务(比如在你的代码示例中:Wait不能内联执行Delay任务,因为没有它的代码)。

你可能会发现我的__ABC0 / await介绍很有用。

根据我从不同来源读到的:

await表达式不会阻塞它正在执行的线程。相反,它会导致编译器注册async方法的其余部分,作为等待任务的延续。控件然后返回给async方法的调用者。当任务完成时,它调用它的延续,async方法的执行从它停止的地方恢复。

要等待单个< >强task < / >强完成,可以调用它的Task.Wait方法。对Wait方法的调用将阻塞调用线程,直到单个类实例完成执行。无参数Wait()方法用于无条件地等待任务完成。该任务通过调用Thread.Sleep方法来模拟工作,使其休眠两秒钟。

这篇文章也是一个很好的读物。

一些重要的事实在其他答案中没有给出:

“异步await"在CIL级别上更加复杂,因此会消耗内存和CPU时间。

如果等待时间不可接受,任何任务都可以取消。

在“异步等待”的情况下;对于这样的任务,我们没有一个处理程序来取消它或监视它。

使用Task比“async await”更灵活。

任何同步功能都可以通过async封装。

public async Task<ActionResult> DoAsync(long id)
{
return await Task.Run(() => { return DoSync(id); } );
}

“异步await"产生很多问题。我们现在没有等待语句将到达没有运行时和上下文调试。如果先等不到,一切都被阻塞了。有时甚至等待似乎到达,但一切都被阻塞:

https://github.com/dotnet/runtime/issues/36063

我不明白为什么我必须生活在代码复制同步和异步方法或使用黑客。

结论:手动创建任务并控制它们要好得多。处理程序给任务更多的控制。我们可以监视任务并管理它们:

https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem

对不起,我的英语不好。