使用await Task.Run()的异步方法从未“完成”

8

我有一个被定义为

public async Task SomeAsyncMethod()
{
   DoSomeStuff();
   await Task.Run(() => {
     DoSomeSyncStuff();
     DoSomeOtherSyncStuff();
   });
   var someDebugVariable = "someDebugValue";
}

这个方法本身完全按照其预定功能运行,一切都很顺利。然而……看起来“外部”的异步Task从未完成。

例如:当我像这样调用它时:

public void CallerMethod()
{
   Task t = SomeAsyncMethod();
   t.Wait();
}

t.Wait() 从未完成。此外:如果我在 someDebugVariable 的赋值处放置断点,它永远不会被触发。

我可以补充说明的是,DoSomeSyncStuffDoSomeOtherSyncStuff 确实执行了它们应该执行的操作,并且通过对它们进行调试,我知道它们都已经完成。

为了证明我的观点,我修改了我的方法如下,结果仍然相同。

public async Task SomeAsyncMethod()
{
   DoSomeStuff();
   await Task.Run(() => {
     /*
     DoSomeSyncStuff();
     DoSomeOtherSyncStuff();
     */
     var a = 2; var b = 3; var c = a + b;
   });
   var someDebugVariable = "someDebugValue";
}

编辑

我尝试过移除除了await Task.Run以外的所有内容,但是没有任何变化。它仍然无法完成。

这个应用程序是一个WPF应用程序。调用线程是UI线程。

我在这里漏掉了什么?


你在单线程的“同步上下文”中调用它吗? - user4003407
在你的例子中,任务实际上从未启动,因此 Wait 永远不会被调用。 - Daniel Kelley
@PetSerAl:我没有使用任何SynchronizationContext。这个调用只是我编写的一个测试方法,用于检查任务是否真的没有完成。 - Hemisphera
你能否在你最后的测试代码中注释掉DoSomeStuff();,以确保问题不是出在这个函数里面? - serhiyb
1
这篇文章与编程有关,讲述了异步代码中不要使用阻塞的问题。对你来说应该很有参考价值。 - sara
显示剩余11条评论
1个回答

17
t.Wait()的调用导致了死锁,并且使异步调用变得毫无意义。我认为,如果你将代码更改为

,问题可以解决。
await Task.Run(() => {
// ...
}).ConfigureAwait(false);
你可以修复死锁并使代码继续运行,但你真的应该摆脱 t.Wait() 调用。所有需要对同步函数调用结果进行操作的事情都应该在等待任务之后完成,而不是在异步函数调用之后完成。
更深入的解释: task.Wait() 会阻塞主线程上所有的执行,同时任务正在运行。当等待任务完成时,它试图返回到主线程,但是主线程被阻塞了!由于 A 正在等待 B,而 B 又在等待 A,所以你会得到一个死锁。
参见:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

实际上,t.Wait 只是测试代码。调用者不是异步的,我需要执行一个异步方法并读取其结果并返回。我没有考虑到死锁问题。我会尝试检查一下... - Hemisphera
@serhiyb 请看我发布的源代码。他使用了死锁这个术语,所以我也使用了。主线程正在等待任务完成,而任务正在等待主线程。死锁。 - Steve
这解决了我的问题。现在我只需要找到罪魁祸首(= 死锁)并弄清楚谁阻塞了谁。 - Hemisphera
1
@Hemisphera 他已经告诉你了。await 等待 UI 线程空闲,然后才完成操作,而 .Wait() 则会阻塞 UI 线程直到 await 完成。你有两个东西在等待另一个完成之前停止等待。 - Scott Chamberlain
亲爱的天堂之主... .ConfigureAwait(false) 也为我解决了问题。我有两个等待,第二个从未解决/完成 - 这个修复了它!谢谢。 - Christopher
旧的解决方案有时仍然是最好的。来自未来的问候! - Kinetic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接