Don ' t Block on Async Code

Source: Internet
Author: User
Tags wrappers

Http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

This is a problem of brought up repeatedly on the forums and Stack Overflow. I think it ' s the most-asked question by async newcomers once they ' ve learned the basics.

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):

  1. The top-level method calls Getjsonasync (within the ui/asp.net context).
  2. Getjsonasync starts the REST request by calling Httpclient.getstringasync (still within the context).
  3. Getstringasync returns an uncompleted Task, indicating the REST request was not complete.
  4. 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.
  5. The top-level method synchronously blocks on the Task returned by Getjsonasync. This blocks the context thread.
  6. ... Eventually, the REST request would complete. This completes the Task is returned by Getstringasync.
  7. 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.
  8. 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:

    1. In your "library" async methods, use Configureawait (false) wherever possible.
    2. Don ' t block on Tasks; Use the async all to down.

Consider the first best practice. Changes to the UI

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. Asp. NET modification

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.

Resources
    • My Introduction to Async/await are a good starting point.
    • Stephen Toub ' s blog post Await, and UI, and deadlocks! Oh, my! Covers this exact type of deadlock (in January of, no less!).
    • If you prefer videos, Stephen Toub demoed This deadlock live (39:40-42:50, but the whole presentation is great!). Lucian Wischik also demoed this deadlock using VB (17:10-19:15).
    • The async/await FAQ goes into detail on exactly when contexts is captured and used for continuations.

This kind of deadlock are always the result of mixing synchronous with asynchronous code.

Usually this is because people was just trying out async with one small piece of code and use synchronous code everywhere Else.

Unfortunately, partially-asynchronous code is much more complex and tricky than just making everything asynchronous.

If you do need to maintain a partially-asynchronous code base, then be sure to check out both more than Stephen Toub ' s blog posts:asynchronous Wrappers for synchronous Methods and synchronous Wrappers for asynchronous Methods, as well as My Asyncex library.

answered Questions

There is scores of answered questions out there that is all caused by the same deadlock problem. It has shown-on WinRT, WPF, Windows Forms, Windows Phone, Monodroid, Monogame, and ASP.

Update (2014-12-01): For more details, see my MSDN article on asynchronous best practices or sections 1.2 in Myconcurrency Cookbook.

There's also a sister article http://blog.stephencleary.com/2012/12/dont-block-in-asynchronous-code.html

Don ' t Block on Async Code

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.