You don't always notice when you use the await and async keywords in C # 5.0. Until today, when debugging an ASP. NET project, it is found that after calling a method that is declared async, the program always gets stuck, even if the task task in the method declared async is executed, the await call of the external method is blocked, and then the following article is discovered, it dawned on the original Awai The use of the T and async patterns can easily cause deadlock in the program, and the following article describes how the await and async mode causes the deadlock of the program through a WinForm example and an ASP. NET example, and how to avoid this deadlock.
Original link
UI Example
Consider the example below. A button click would initiate a REST call and display the results in a text box (this sample is for Windows Forms, but the Same principles apply to any UI application).
//My "Library" method. Public Static AsyncTask<jobject>Getjsonasync (Uri uri) {using(varClient =NewHttpClient ()) { varJsonstring =awaitclient. Getstringasync (URI); returnJobject.parse (jsonstring); }}//My "Top-level" method. Public voidButton1_Click (...) { varJsontask =Getjsonasync (...); TextBox1.Text=Jsontask.result;}
The "Getjson" helper method takes care of making, the actual REST call and parsing it as JSON. The button click handler waits for the helper method to complete and then displays its results.
This code would deadlock.
ASP. Example
This example is very similar; We have a library method of performs a REST call and only this time it's used in an ASP. But the same principles apply to any ASP. Application):
//My "Library" method. Public Static AsyncTask<jobject>Getjsonasync (Uri uri) {using(varClient =NewHttpClient ()) { varJsonstring =awaitclient. Getstringasync (URI); returnJobject.parse (jsonstring); }}//My "Top-level" method. Public classmycontroller:apicontroller{ Public stringGet () {varJsontask =Getjsonasync (...); returnjsonTask.Result.ToString (); }}
This code would also deadlock. For the same reason.
What causes the Deadlock
Here's the situation:remember from my intro post, which after you await a Task, when the method continues it would continue In a context.
In the first case, this context was a UI context (which applies to any UI except Console applications). In the second case, this context was an ASP.
One other important Point:an ASP. NET request context is not tied to a specific thread (like the UI context was), but it does only allow one thread in at atime. This interesting aspect isn't officially documented anywhere AFAIK, but it's mentioned in my MSDN article about Synchron Izationcontext.
So that's what happens, starting with the top-level method (button1_click for Ui/mycontroller.get for ASP):
- The top-level method calls Getjsonasync (within the ui/asp.net context).
- Getjsonasync starts the REST request by calling Httpclient.getstringasync (still within the context).
- Getstringasync returns an uncompleted Task, indicating the REST request was not complete.
- Getjsonasync awaits the Task returned by Getstringasync. The context is captured and would be used to continue running the Getjsonasync method later. Getjsonasync returns an uncompleted Task, indicating that the Getjsonasync method isn't complete.
- The top-level method synchronously blocks on the Task returned by Getjsonasync. This blocks the context thread.
- ... Eventually, the REST request would complete. This completes the Task is returned by Getstringasync.
- The continuation for Getjsonasync are now ready to run, and it waits for the context to being available so it can execute in t He context.
- Deadlock. The top-level method is blocking the context thread, waiting for Getjsonasync to complete, and Getjsonasync are waiting for The context to is 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. This type of deadlock can is caused for either "context".
Preventing the Deadlock
There is the best practices (both covered in my intro post) that avoid this situation:
- In your "library" async methods, use Configureawait (false) wherever possible.
- Don ' t block on Tasks; Use the async all to down.
Here I add that if you are developing a WinForm program, it is better to use the second method to avoid deadlocks, i.e. do not block the main thread (the context thread mentioned in this article) so that when the await task object thread finishes executing, the main thread is not blocked , so the code behind the await will continue to execute on the main thread. The first method is not recommended in WinForm because the first method causes the code behind the await to execute on a new thread, and if there is code that sets the value of the WinForm control after the await, it can cause a thread-safety problem for the WinForm program. So the best way in WinForm is not to block the main thread, so that the code behind the await can be executed on the main thread. However, there is no thread safety problem with the first or second method above in ASP.
Consider the first best practice. The new "library" method looks like this:
Public Static Async task<jobject> Getjsonasync (Uri uri) { using (varnew HttpClient ()) { varawait client.} Getstringasync (URI). Configureawait (false); return Jobject.parse (jsonstring); }}
This changes the continuation behavior of Getjsonasync so, it does not resume on the context. Instead, Getjsonasync would resume on a thread pool thread. This enables Getjsonasync to complete the Task it returned without have to re-enter the context.
Consider the second best practice. The new "top-level" methods look like this:
Public Async voidButton1_Click (...) { varJSON =awaitGetjsonasync (...); TextBox1.Text=JSON;} Public classmycontroller:apicontroller{ Public Asynctask<string>Get () {varJSON =awaitGetjsonasync (...); returnJSON. ToString (); }}
This changes the blocking behavior of the top-level methods so, the context is never actually blocked; All "Waits" is "asynchronous waits".
Note: It's best for apply both best practices. Either one would prevent the deadlock, but both must is applied to achieve maximum performance and responsiveness.
Finally, add that the await and async deadlock problem mentioned in this article does not exist in the. NET console program. Because after the experiment, we found out. NET console program, the code behind the await is executed on a new thread by default, that is, if task.configureawait (false) is not called in the console program, the code behind the await will be executed on a newly-started thread. There is no deadlock with the main thread. However, deadlocks can occur in WinForm and ASP.
Beware of the deadlock caused by await in C # 5.0 and Async mode