Original link
Https://www.cnblogs.com/OpenCoder/p/4434574.html
Content
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 async task<jobject> Getjsonasync (Uri uri) { using (var client = new Httpclie NT ()) { var jsonstring = await client. Getstringasync (URI); Return Jobject.parse (jsonstring);} } My "top-level" method.public void button1_click (...) { var jsontask = 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 async task<jobject> Getjsonasync (Uri uri) { using (var client = new Httpclie NT ()) { var jsonstring = await client. Getstringasync (URI); Return Jobject.parse (jsonstring);} } My "top-level" Method.public class mycontroller:apicontroller{public string Get () { var jsontask = Getjs Onasync (...); return jsonTask.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 doesonly 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, here's within the context table It is shown that the Getjsonasync method is still executed by the thread executing top-level methods, which is the main thread).
- Getstringasync returns an uncompleted Task, indicating the REST request was not complete.
- Getjsonasync awaits the Task returned by Getstringasync. The context (the context here still refers to the thread that executes top-level method, and Getjsonasync continues to execute top-level after the Getstringasync methods have been executed) Method's thread to execute the code after the await keyword, which is why the code in this case is deadlocked) is captured and will being 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".
The general meaning of the above is that when you use await and async mode, the block of code behind this line of the AWAIT keyword is continued by a context (that is, the ASP. NET request Contex and UI context) thread that is mentioned above. If we call top-level method in this case a thread called thread A (that is, the context thread), because the Getjsonasync methods are also called by thread A, So when the Getstringasync method of await in the Getjsonasync method finishes executing, Getjsonasync needs to re-use thread A to execute the code after the await code line, and now because thread A is in the top-level The code in method is blocked because the jsontask.result is accessed (because thread a calls top-level When the method code is Jsontask.result, the await Getstringasync task is not completed, so it is blocked by thread A, so getjsonasync cannot re-use thread A to execute the code block after the await code line, and it is blocked, so it forms a dead Lock. That is, the top-level method code thread A is blocked while waiting for the getstringasync end of an await in Getjsonasync, and Getstringasync is waiting for threads A to top-level Method's blocking end gets thread A to execute Getjsonasync the code behind the await line of code is also blocked, two blocks waiting for each other, deadlock with each other.
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.
- If you want to end the call to Async & await mode, start a new thread to await the return result of the Async method
Here I add that if you are developing a WinForm program, it is better to use the second method to avoid deadlocks, that is, do not block the main thread, so that when the await task object thread finishes executing, because the main thread is not blocked, So the code behind the await will be at the right time (the "right time" mentioned here is the. NET Framework's own judgment, and the. NET Framework will schedule the main thread to continue executing the code at a later time 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 different thread, rather than executing on the main threads, and if there is code that sets the value of the WinForm control after an await, it can cause a thread-safety problem for the WinForm program , so the best approach in WinForm is not to block the main thread, allowing the code behind the await to execute 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 (var client = new HttpClient ()) { var js Onstring = await 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 void button1_click (...) { var json = await getjsonasync (...); TextBox1.Text = JSON;} public class mycontroller:apicontroller{Public async task<string> Get () { var json = await Getjso Nasync (...); Return JSON. 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.
The third best practice: If you want to end the call to Async & await mode, start a new thread to await the return result of the Async method
My "library" method.public static async task<jobject> Getjsonasync (Uri uri) { using (var client = new Httpclie NT ()) { var jsonstring = await client. Getstringasync (URI); Return Jobject.parse (jsonstring);} } My "top-level" method.public string Get () { string jsonresultstring = string. Empty; Task.run (Async () = { jsonresultstring = await getjsonasync (...); }). Wait ();//Here The thread is started to prevent the Async & await mode from causing a deadlock return jsonresultstring;}
This is because the Getjsonasync method is called by the Task.run new startup thread, so the await Getjsonasync (...) After execution, the. Net Framework uses Task.run newly-started thread to execute the code after the await and does not block each other from the thread of top-level method (that is, the context thread), causing a deadlock.
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 this line of the await keyword is executed on a new thread by default, that is, if task.configureawait (false) is not called in the console program, The code behind this line of the await keyword is also executed on a newly-started thread, and does not deadlock with the main threads. However, deadlocks can occur in WinForm and ASP.
[Turn] beware of deadlocks in C # 5.0 with await and async mode