首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >导致死锁的异步/等待示例

导致死锁的异步/等待示例
EN

Stack Overflow用户
提问于 2013-02-22 17:52:30
回答 5查看 67.3K关注 0票数 108

我遇到了一些使用c#的async/await关键字进行异步编程的最佳实践(我是c#5.0的新手)。

其中一个建议是:

稳定性:了解您的同步上下文

..。某些同步上下文是不可重入的和单线程的。这意味着在给定时间只能在上下文中执行一个工作单元。这方面的一个例子是Windows UI线程或ASP.NET请求上下文。在这些单线程同步上下文中,很容易导致自己死锁。如果您从单线程上下文中派生出一个任务,然后在该上下文中等待该任务,则等待代码可能会阻塞后台任务。

代码语言:javascript
复制
public ActionResult ActionAsync()
{
    // DEADLOCK: this blocks on the async task
    var data = GetDataAsync().Result;

    return View(data);
}

private async Task<string> GetDataAsync()
{
    // a very simple async method
    var result = await MyWebService.GetDataAsync();
    return result.ToString();
}

如果我尝试自己分析它,主线程会在MyWebService.GetDataAsync();中产生一个新的线程,但由于主线程在那里等待,所以它在GetDataAsync().Result中等待结果。同时,假设数据已准备就绪。为什么主线程不继续它的延续逻辑并从GetDataAsync()返回一个字符串结果?

有人能给我解释一下为什么上面的例子会出现死锁吗?我完全不知道问题出在哪里...

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-02-22 18:37:37

看看this example,斯蒂芬给你一个明确的答案:

这就是发生的事情,从顶层方法开始(对于UI为Button1_Click,对于ASP.NET为MyController.Get ):

在UI/ASP.NET context).

  • GetJsonAsync中,顶级方法调用GetJsonAsync (在UI/ASP.NET context).

  • GetJsonAsync中,通过调用Task来启动REST请求)(仍然在complete.

  • GetJsonAsync中,返回一个未完成的Task,指示REST请求不是GetJsonAsync,等待GetStringAsync返回的Task。该上下文将被捕获,稍后将用于继续运行GetJsonAsync方法。GetJsonAsync返回未完成的Task,表示GetJsonAsync方法不是GetJsonAsync返回的Task上同步阻塞的顶级方法。这会阻止上下文线程。

  • ...最终,REST请求将完成。这样就完成了GetStringAsync.

  • The continuation返回的TaskGetJsonAsync现在可以运行了,它会等待上下文可用,以便可以在context.

  • Deadlock.中执行顶层方法阻塞上下文线程,等待GetJsonAsync完成,而GetJsonAsync等待上下文释放,以便它可以完成。对于UI示例," context“是UI上下文;对于ASP.NET示例,"context”是ASP.NET请求上下文。这种类型的死锁可能是由于两个“上下文”中的任何一个造成的。

你应该阅读的另一个链接:

票数 89
EN

Stack Overflow用户

发布于 2017-05-11 18:01:23

  • 事实1:GetDataAsync().Result;将在GetDataAsync()返回的任务完成时运行,同时它会阻塞UI线程
  • 事实2:等待的延续(return result.ToString())被排队到UI线程中以供执行
  • 事实3:GetDataAsync()返回的任务将在其排队的延续运行时完成
  • 事实4:由于UI线程被阻塞(Fact 1)

死锁!

可以通过提供替代方案来打破死锁,以避免事实1或事实2。

等待等待避免1,4。使用var data = await GetDataAsync()代替阻塞UI线程,它允许UI线程保持等待2,3。将等待的继续排队到未被阻塞的不同线程,例如使用var data = Task.Run(GetDataAsync).Result,它将继续发送到线程池线程的同步上下文。这样就可以完成GetDataAsync()返回的任务。

这在他使用DelayAsync()示例的article by Stephen Toub中得到了很好的解释。

票数 27
EN

Stack Overflow用户

发布于 2018-06-29 17:12:49

我只是在一个ASP.NET MVC项目中再次处理了这个问题。当您想要从PartialView调用async方法时,不允许使用PartialView async。如果你这样做了,你会得到一个异常。

在要从同步方法调用async方法的情况下,您可以使用以下简单的解决方法:

在调用之前,清除SynchronizationContext

  • Do

  • 调用,这里不会再有死锁,等待它finish

  • Restore SynchronizationContext

示例:

代码语言:javascript
复制
public ActionResult DisplayUserInfo(string userName)
{
    // trick to prevent deadlocks of calling async method 
    // and waiting for on a sync UI thread.
    var syncContext = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext(null);

    //  this is the async call, wait for the result (!)
    var model = _asyncService.GetUserInfo(Username).Result;

    // restore the context
    SynchronizationContext.SetSynchronizationContext(syncContext);

    return PartialView("_UserInfo", model);
}
票数 25
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15021304

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档