我遇到了一些使用c#的async
/await
关键字进行异步编程的最佳实践(我是c#5.0的新手)。
其中一个建议是:
稳定性:了解您的同步上下文
..。某些同步上下文是不可重入的和单线程的。这意味着在给定时间只能在上下文中执行一个工作单元。这方面的一个例子是Windows UI线程或ASP.NET请求上下文。在这些单线程同步上下文中,很容易导致自己死锁。如果您从单线程上下文中派生出一个任务,然后在该上下文中等待该任务,则等待代码可能会阻塞后台任务。
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()
返回一个字符串结果?
有人能给我解释一下为什么上面的例子会出现死锁吗?我完全不知道问题出在哪里...
发布于 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
上同步阻塞的顶级方法。这会阻止上下文线程。
GetStringAsync
.
Task
,GetJsonAsync
现在可以运行了,它会等待上下文可用,以便可以在context.
GetJsonAsync
完成,而GetJsonAsync
等待上下文释放,以便它可以完成。对于UI示例," context“是UI上下文;对于ASP.NET示例,"context”是ASP.NET请求上下文。这种类型的死锁可能是由于两个“上下文”中的任何一个造成的。你应该阅读的另一个链接:
发布于 2017-05-11 18:01:23
GetDataAsync().Result;
将在GetDataAsync()
返回的任务完成时运行,同时它会阻塞UI线程return result.ToString()
)被排队到UI线程中以供执行GetDataAsync()
返回的任务将在其排队的延续运行时完成死锁!
可以通过提供替代方案来打破死锁,以避免事实1或事实2。
等待等待避免1,4。使用var data = await GetDataAsync()
代替阻塞UI线程,它允许UI线程保持等待2,3。将等待的继续排队到未被阻塞的不同线程,例如使用var data = Task.Run(GetDataAsync).Result
,它将继续发送到线程池线程的同步上下文。这样就可以完成GetDataAsync()
返回的任务。
这在他使用DelayAsync()
示例的article by Stephen Toub中得到了很好的解释。
发布于 2018-06-29 17:12:49
我只是在一个ASP.NET MVC项目中再次处理了这个问题。当您想要从PartialView
调用async
方法时,不允许使用PartialView
async
。如果你这样做了,你会得到一个异常。
在要从同步方法调用async
方法的情况下,您可以使用以下简单的解决方法:
在调用之前,清除SynchronizationContext
SynchronizationContext
示例:
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);
}
https://stackoverflow.com/questions/15021304
复制相似问题