导致死锁的异步/等待示例 [英] An async/await example that causes a deadlock
问题描述
我遇到了一些使用 c# 的 async
/await
关键字进行异步编程的最佳实践(我是 c# 5.0 的新手).
I came across some best practices for asynchronous programming using c#'s async
/await
keywords (I'm new to c# 5.0).
给出的建议之一如下:
稳定性:了解您的同步上下文
...一些同步上下文是不可重入的和单线程的.这意味着在给定时间只能在上下文中执行一个工作单元.这方面的一个示例是 Windows UI 线程或 ASP.NET 请求上下文.在这些单线程同步上下文中,很容易让自己陷入死锁.如果您从单线程上下文中生成任务,然后在上下文中等待该任务,您的等待代码可能会阻塞后台任务.
... Some synchronization contexts are non-reentrant and single-threaded. This means only one unit of work can be executed in the context at a given time. An example of this is the Windows UI thread or the ASP.NET request context. In these single-threaded synchronization contexts, it’s easy to deadlock yourself. If you spawn off a task from a single-threaded context, then wait for that task in the context, your waiting code may be blocking the background task.
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()
返回一个字符串结果?
If I try to dissect it myself, the main thread spawns to a new one in MyWebService.GetDataAsync();
, but since the main thread awaits there, it waits on the result in GetDataAsync().Result
. Meanwhile, say the data is ready. Why doesn't the main thread continue it's continuation logic and returns a string result from GetDataAsync()
?
谁能解释一下为什么上面的例子会出现死锁?我完全不知道问题是什么......
Can someone please explain me why there is a deadlock in the above example? I'm completely clueless about what the problem is ...
推荐答案
查看 这个例子,Stephen 给你一个明确的答案:
Take a look at this example, Stephen has a clear answer for you:
这就是发生的事情,从顶级方法开始(Button1_Click
用于 UI/MyController.Get
用于 ASP.NET):
So this is what happens, starting with the top-level method (
Button1_Click
for UI /MyController.Get
for ASP.NET):
顶级方法调用
GetJsonAsync
(在 UI/ASP.NET 上下文中).
The top-level method calls
GetJsonAsync
(within the UI/ASP.NET context).
GetJsonAsync
通过调用 HttpClient.GetStringAsync
(仍在上下文中)启动 REST 请求.
GetJsonAsync
starts the REST request by calling HttpClient.GetStringAsync
(still within the context).
GetStringAsync
返回未完成的Task
,表示REST请求未完成.
GetStringAsync
returns an uncompleted Task
, indicating the REST request is not complete.
GetJsonAsync
等待 GetStringAsync
返回的 Task
.上下文被捕获,稍后将用于继续运行 GetJsonAsync
方法.GetJsonAsync
返回一个未完成的Task
,说明GetJsonAsync
方法没有完成.
GetJsonAsync
awaits the Task
returned by GetStringAsync
. The context is captured and will be used to continue running the GetJsonAsync
method later. GetJsonAsync
returns an uncompleted Task
, indicating that the GetJsonAsync
method is not complete.
顶级方法同步阻塞 GetJsonAsync
返回的 Task
.这会阻塞上下文线程.
The top-level method synchronously blocks on the Task
returned by GetJsonAsync
. This blocks the context thread.
... 最终,REST 请求将完成.这完成了 GetStringAsync
返回的 Task
.
... Eventually, the REST request will complete. This completes the Task
that was returned by GetStringAsync
.
GetJsonAsync
的延续现在可以运行了,它会等待上下文可用,以便在上下文中执行.
The continuation for GetJsonAsync
is now ready to run, and it waits for the context to be available so it can execute in the context.
死锁.顶层方法正在阻塞上下文线程,等待 GetJsonAsync
完成,而 GetJsonAsync
正在等待上下文空闲以便它可以完成.对于 UI 示例,上下文"是 UI 上下文;对于 ASP.NET 示例,上下文"是 ASP.NET 请求上下文.任何一种上下文"都可能导致这种类型的死锁.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync
to complete, and GetJsonAsync
is waiting for the context to be free so it can complete. For the UI example, the "context" is the UI context; for the ASP.NET example, the "context" is the ASP.NET request context. This type of deadlock can be caused for either "context".
您应该阅读的另一个链接:等待、UI 和死锁!哦,天哪!
Another link you should read: Await, and UI, and deadlocks! Oh my!
这篇关于导致死锁的异步/等待示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!