await vs Task.Wait - Deadlock? 在异步编程中,我们经常会遇到使用`await`关键字或`Task.Wait`方法来等待异步操作完成的情况。然而,这两种方式在使用上有一些区别,并且在特定情况下可能会导致死锁问题。 `await`关键字用于等待一个异步操作完成,并返回其结果。使用`await`时,当前线程会暂时挂起,并且允许其他操作在此期间执行。一旦异步操作完成,当前线程会恢复执行。 `Task.Wait`方法也用于等待异步操作完成,但它是一个同步方法,会导致当前线程阻塞,
await vs Task.Wait - Deadlock? 在异步编程中,我们经常会遇到使用`await`关键字或`Task.Wait`方法来等待异步操作完成的情况。然而,这两种方式在使用上有一些区别,并且在特定情况下可能会导致死锁问题。 `await`关键字用于等待一个异步操作完成,并返回其结果。使用`await`时,当前线程会暂时挂起,并且允许其他操作在此期间执行。一旦异步操作完成,当前线程会恢复执行。 `Task.Wait`方法也用于等待异步操作完成,但它是一个同步方法,会导致当前线程阻塞,
我不太明白Task.Wait
和await
之间的区别。\n我在一个ASP.NET WebAPI服务中有类似如下函数:\n
public class TestController : ApiController { public static async TaskFoo() { await Task.Delay(1).ConfigureAwait(false); return ""; } public async static Task Bar() { return await Foo(); } public async static Task Ros() { return await Bar(); } // GET api/test public IEnumerable Get() { Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray()); return new string[] { "value1", "value2" }; // 这段代码永远不会执行 } }
\n其中Get
会造成死锁。\n是什么导致了这个问题?为什么使用阻塞等待而不是await Task.Delay
时不会出现问题?
在讨论(async/await vs Task.Wait - Deadlock?)这个问题之前,需要了解一些重要的事实:
- async/await在CIL级别上更复杂,因此会消耗更多的内存和CPU时间。
- 如果等待时间不可接受,任何任务都可以被取消。
- 在async/await的情况下,我们没有一个处理程序来取消或监视这样的任务。
- 使用Task比async/await更灵活。
- 任何同步功能都可以被async包装。
下面是一个示例代码:
public async TaskDoAsync(long id) { return await Task.Run(() => { return DoSync(id); }); }
async/await会产生许多问题。我们无法确定await语句是否会在运行时和上下文调试中被执行到。如果第一个await没有被执行到,一切都会被阻塞。有时即使await似乎被执行到了,仍然会发生阻塞,如下链接所示:
https://github.com/dotnet/runtime/issues/36063
我不明白为什么我必须为同步和异步方法而重复编写代码或使用hack。
结论:手动创建任务并对其进行控制要更好。对Task的处理程序提供了更多的控制。我们可以监视任务并对其进行管理,如下链接所示:
https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem
对于我的英语表示抱歉。
在这篇文章中,我们将讨论一个常见的问题:await和Task.Wait之间的死锁问题。首先,让我们看一下这个问题是如何产生的以及如何解决。
根据我从不同的来源中所了解到的:
await表达式不会阻塞它所在的线程。相反,它会使编译器将异步方法的其余部分作为继续处理等待的任务。控制权然后返回给异步方法的调用者。当任务完成时,它会调用其继续处理程序,并且异步方法会从离开的地方继续执行。
要等待单个任务完成,可以调用其Task.Wait方法。调用Wait方法会阻塞调用线程,直到单个任务实例完成执行。无参数的Wait()方法用于无条件地等待任务完成。任务通过调用Thread.Sleep方法来模拟工作,以便休眠两秒钟。
这篇文章也是一个很好的阅读材料。
“那这不是技术上不正确吗?有人可以请说明一下吗?”-我可以说明一下吗?你是在问一个问题吗?(我只是想明确一下你是在问还是在回答)。如果你是在问:它可能作为一个单独的问题更好;它不太可能在这里作为一个答案收集到新的回答。
我已经回答了问题,并在这里提出了一个我所怀疑的问题的单独问题。非常感谢。你能不能请取消你对答案的删除投票?
“你能不能请取消你对答案的删除投票?”-那不是我的,感谢,任何我投的票都会立即生效。然而,我认为这个答案没有回答问题的关键点,即死锁行为。
这是不正确的。在达到第一个await之前,一切都被阻塞了。尽管你是对的,这并不意味着答案是没有用的。
使用async await在技术上不会创建一个线程,并且会导致您的代码共享线程,而不是Task/Thread。异步用于不阻塞UI,线程用于非UI阻塞逻辑。
在解决await和Task.Wait之间的死锁问题时,需要注意到Task.Wait方法会阻塞当前线程,直到任务完成,而await表达式不会阻塞当前线程,而是将剩余的异步方法作为继续处理程序。因此,如果在异步方法中使用了Task.Wait方法,可能会导致死锁。为了解决这个问题,应该避免在异步方法中使用Task.Wait方法,而是使用await表达式来等待任务的完成。
希望这篇文章能帮助你理解await和Task.Wait之间的死锁问题以及如何解决它。谢谢阅读!
(await vs Task.Wait - Deadlock?)
在使用异步编程中,Wait
和 await
的概念相似,但实际上完全不同。
Wait
方法会同步地阻塞线程,直到任务完成。这意味着当前线程会被阻塞,等待任务完成。通常情况下,应该遵循“从头到尾都使用异步”的原则,也就是说,不要在异步代码中阻塞。在我的博客中,我详细介绍了阻塞异步代码会导致死锁的细节。
await
关键字会异步等待任务完成。这意味着当前的方法会被“暂停”(其状态会被捕获),并返回一个未完成的任务给调用者。稍后,当await
表达式完成时,剩余的方法会被作为一个继续任务调度执行。
你还提到了“协作阻塞”,我猜你是指你正在等待的任务可能在等待线程上执行。确实存在这种情况,但这是一种优化。在许多情况下,这是不可能的,比如任务是为另一个调度器准备的,或者任务已经开始执行,或者任务是一个非代码任务(就像你的代码示例中的Delay
任务,Wait
不能在内联执行Delay
任务,因为没有相关代码)。
你可能会发现我在博客中介绍的async
/await
入门文章有所帮助。
我认为可能存在误解,Wait
方法可以正常工作,而await
方法会导致死锁。
不,任务调度器不会这样做。 Wait
方法会阻塞线程,而且不能用于其他目的。
我猜你只是弄混了方法名称,导致阻塞代码出现死锁,并且使用await
的代码能够正常工作。要么就是死锁与这两者无关,你对问题的诊断有误。
这是设计上的问题——它在UI应用程序中效果很好,但对于ASP.NET应用程序来说可能会有些妨碍。ASP.NET Core通过移除SynchronizationContext
来解决了这个问题,所以在ASP.NET Core请求中进行阻塞不再导致死锁。
这真是太奇妙了。我来这里是因为我在理解《C#并发编程实用指南》中的一部分时遇到了一些问题,然后过了一段时间我才意识到你就是作者。