Async方法死锁的主题素材

今日调节和测试requet.GetRequestStreamAsync异步方法现身不回去的标题,恐怕是死锁了。看到老外壹篇作品解释了异步方法死锁的难点,懒的翻译,间接搬过来了。

今日调节和测试requet.GetRequestStreamAsync异步方法出现不回来的主题素材,也许是死锁了。看到老外一篇小说解释了异步方法死锁的难点,懒的翻译,直接搬过来了。

Async方法死锁的题目 Don’t Block on Async Code(转),asyncblock

今日调节和测试requet.GetRequestStreamAsync异步方法出现不回去的主题材料,大概是死锁了。看到老外1篇作品解释了异步方法死锁的标题,懒的翻译,直接搬过来了。

This is a problem that is 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.

This is a problem that is 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 will 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 HttpClient())
  {
    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 will deadlock.

This is a problem that is 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.

This is a problem that is 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 will 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 HttpClient())
  {
    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 will deadlock.

 

ASP.NET Example

This example is very similar; we have a library method that performs a
REST call, only this time it’s used in an ASP.NET context (Web API in
this case, but the same principles apply to anyASP.NET application):

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public class MyController : ApiController
{
  public string Get()
  {
    var jsonTask = GetJsonAsync(...);
    return jsonTask.Result.ToString();
  }
}

This code will also deadlock. For the same reason.

UI Example

Consider the example below. A button click will 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 HttpClient())
  {
    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 will deadlock.

UI Example

Consider the example below. A button click will 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 HttpClient())
  {
    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 will deadlock.

ASP.NET Example

This example is very similar; we have a library method that performs a
REST call, only this time it’s used in an ASP.NET context (Web API in
this case, but the same principles apply to any ASP.NET application):

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public class MyController : ApiController
{
  public string Get()
  {
    var jsonTask = GetJsonAsync(...);
    return jsonTask.Result.ToString();
  }
}

This code will also deadlock. For the same reason.

 

 

What Causes the Deadlock

Here’s the situation: remember from my intro post that after you await a
Task, when the method continues it will continue in a context.

In the first case, this context is a UI context (which applies
to any UI except Console applications). In the second case, this
context is an ASP.NET request context.

One other important point: an ASP.NET request context is not tied to a
specific thread (like the UI context is), but it does only allow one
thread in at a time. This interesting aspect is not officially
documented anywhere AFAIK, but it is mentioned in my MSDN article about
SynchronizationContext.

So this is what happens, starting with the top-level method
(Button1_Click for UI / MyController.Get for ASP.NET):

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”.

ASP.NET Example

This example is very similar; we have a library method that performs a
REST call, only this time it’s used in an ASP.NET context (Web API in
this case, but the same principles apply to anyASP.NET application):

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public class MyController : ApiController
{
  public string Get()
  {
    var jsonTask = GetJsonAsync(...);
    return jsonTask.Result.ToString();
  }
}

This code will also deadlock. For the same reason.

ASP.NET Example

This example is very similar; we have a library method that performs a
REST call, only this time it’s used in an ASP.NET context (Web API in
this case, but the same principles apply to anyASP.NET application):

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public class MyController : ApiController
{
  public string Get()
  {
    var jsonTask = GetJsonAsync(...);
    return jsonTask.Result.ToString();
  }
}

This code will also deadlock. For the same reason.

What Causes the Deadlock

Here’s the situation: remember from my intro
post that
after you await a Task, when the method continues it will continue in a
context
.

In the first case, this context is a UI context (which applies
to any UI except Console applications). In the second case, this
context is an ASP.NET request context.

One other important point: an ASP.NET request context is not tied to a
specific thread (like the UI context is), but it does only allow one
thread in at a time. This interesting aspect is not officially
documented anywhere AFAIK, but it is mentioned in my MSDN article about
SynchronizationContext.

So this is what happens, starting with the top-level method
(Button1_Click for UI / MyController.Get for ASP.NET):

  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 is not complete.
  4. 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.
  5. The top-level method synchronously blocks on the Task returned by
    GetJsonAsync. This blocks the context thread.
  6. … Eventually, the REST request will complete. This completes the
    Task that was returned by GetStringAsync.
  7. 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.
  8. 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”.

 

Async方法死锁的主题素材。Preventing the Deadlock

There are two best practices (both covered in my intro post) that avoid
this situation:

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 jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
    return JObject.Parse(jsonString);
  }
}

This changes the continuation behavior of GetJsonAsync so that it
does not resume on the context. Instead, GetJsonAsync will resume on a
thread pool thread. This enables GetJsonAsync to complete the Task it
returned without having to re-enter the context.

 

Using ConfigureAwait(false) to avoid deadlocks is a dangerous
practice. You would have to
use ConfigureAwait(false) for every await in the transitive
closure of all methods called by the blocking code, including all
third- and second-party code
. Using ConfigureAwait(false) to avoid
deadlock is at best just a hack).

As the title of this post points out, the better solution is “Don’t
block on async code”.

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 GetJsonAsync(...);
    return json.ToString();
  }
}

This changes the blocking behavior of the top-level methods so that the
context is never actually blocked; all “waits” are “asynchronous waits”.

Note: It is best to apply both best practices. Either one will
prevent the deadlock, but both must be applied to achieve maximum
performance and responsiveness.

What Causes the Deadlock

Here’s the situation: remember from my intro
post that
after you await a Task, when the method continues it will continue in a
context
.

In the first case, this context is a UI context (which applies
to any UI except Console applications). In the second case, this
context is an ASP.NET request context.

One other important point: an ASP.NET request context is not tied to a
specific thread (like the UI context is), but it does only allow one
thread in at a time. This interesting aspect is not officially
documented anywhere AFAIK, but it is mentioned in my MSDN article about
SynchronizationContext.

So this is what happens, starting with the top-level method
(Button1_Click for UI / MyController.Get for ASP.NET):

  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 is not complete.
  4. 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.
  5. The top-level method synchronously blocks on the Task returned by
    GetJsonAsync. This blocks the context thread.
  6. … Eventually, the REST request will complete. This completes the
    Task that was returned by GetStringAsync.
  7. 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.
  8. 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”.

What Causes the Deadlock

Here’s the situation: remember from my intro
post that
after you await a Task, when the method continues it will continue in a
context
.

In the first case, this context is a UI context (which applies
to any UI except Console applications). In the second case, this
context is an ASP.NET request context.

One other important point: an ASP.NET request context is not tied to a
specific thread (like the UI context is), but it does only allow one
thread in at a time. This interesting aspect is not officially
documented anywhere AFAIK, but it is mentioned in my MSDN article about
SynchronizationContext.

So this is what happens, starting with the top-level method
(Button1_Click for UI / MyController.Get for ASP.NET):

  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 is not complete.
  4. 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.
  5. The top-level method synchronously blocks on the Task returned by
    GetJsonAsync. This blocks the context thread.
  6. … Eventually, the REST request will complete. This completes the
    Task that was returned by GetStringAsync.
  7. 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.
  8. 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”.

Preventing the Deadlock

There are two 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 async all the way down.

Consider the first best practice.   //UI的修改

The new “library” method looks like this:

public static async Task<JObject> GetJsonAsync(Uri uri)
{
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
    return JObject.Parse(jsonString);
  }
}

This changes the continuation behavior of GetJsonAsync so that it
does not resume on the context.

Instead, GetJsonAsync will resume on a thread pool thread.

This enables GetJsonAsync to complete the Task it returned without
having to re-enter the context.

 

Consider the second best practice.     //ASP.NET的修改

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 GetJsonAsync(...);
    return json.ToString();
  }
}

This changes the blocking behavior of the top-level methods so that the
context is never actually blocked; all “waits” are “asynchronous waits”.

Note: It is best to apply both best practices. Either one will
prevent the deadlock, but both must be applied to achieve maximum
performance and responsiveness.

Resources

  • My introduction to async/await is 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 2011, 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 are
    captured and used for continuations.

This kind of deadlock is always the result of mixing synchronous with
asynchronous code. Usually this is because people are 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 two more of Stephen Toub’s blog posts: Asynchronous
Wrappers for Synchronous Methods and Synchronous Wrappers for
Asynchronous Methods, as well as my AsyncEx library.

Dont Block on Async
Code(转),asyncblock
今日调节和测试requet.GetRequestStreamAsync异步方法出现不回去的标题,也许是死锁了。看到老…

Preventing the Deadlock

There are two 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 async all the way down.

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 jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
    return JObject.Parse(jsonString);
  }
}

This changes the continuation behavior of GetJsonAsync so that it
does not resume on the context. Instead, GetJsonAsync will resume on a
thread pool thread. This enables GetJsonAsync to complete the Task it
returned without having to re-enter the context.

 

Using ConfigureAwait(false) to avoid deadlocks is a dangerous
practice. You would have to
use ConfigureAwait(false) for every await in the transitive
closure of all methods called by the blocking code, including all
third- and second-party code
. Using ConfigureAwait(false) to avoid
deadlock is at best just a
hack).

As the title of this post points out, the better solution is “Don’t
block on async code”.

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 GetJsonAsync(...);
    return json.ToString();
  }
}

This changes the blocking behavior of the top-level methods so that the
context is never actually blocked; all “waits” are “asynchronous waits”.

Note: It is best to apply both best practices. Either one will
prevent the deadlock, but both must be applied to achieve maximum
performance and responsiveness.

Preventing the Deadlock

There are two 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 async all the way down.

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 jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
    return JObject.Parse(jsonString);
  }
}

This changes the continuation behavior of GetJsonAsync so that it
does not resume on the context. Instead, GetJsonAsync will resume on a
thread pool thread. This enables GetJsonAsync to complete the Task it
returned without having to re-enter the context.

 

Using ConfigureAwait(false) to avoid deadlocks is a dangerous
practice. You would have to
use ConfigureAwait(false) for every await in the transitive
closure of all methods called by the blocking code, including all
third- and second-party code
. Using ConfigureAwait(false) to avoid
deadlock is at best just a
hack).

As the title of this post points out, the better solution is “Don’t
block on async code”.

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 GetJsonAsync(...);
    return json.ToString();
  }
}

This changes the blocking behavior of the top-level methods so that the
context is never actually blocked; all “waits” are “asynchronous waits”.

Note: It is best to apply both best practices. Either one will
prevent the deadlock, but both must be applied to achieve maximum
performance and responsiveness.

Resources

  • My introduction to
    async/await is
    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 2011, 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 are captured and used for
    continuations.

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

Usually this is because people are 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 two more of Stephen Toub’s blog posts:Asynchronous
Wrappers for Synchronous
Methods and Synchronous
Wrappers for Asynchronous
Methods,
as well as my AsyncEx library.

Resources

  • My introduction to
    async/await is
    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 2011, 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 are captured and used for
    continuations.

This kind of deadlock is always the result of mixing synchronous with
asynchronous code. Usually this is because people are 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 two more of Stephen Toub’s blog posts: Asynchronous
Wrappers for Synchronous
Methods and Synchronous
Wrappers for Asynchronous
Methods,
as well as my AsyncEx library.

Resources

  • My introduction to
    async/await is
    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 2011, 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 are captured and used for
    continuations.

This kind of deadlock is always the result of mixing synchronous with
asynchronous code. Usually this is because people are 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 two more of 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 are scores of answered questions out there that are all caused by
the same deadlock problem. It has shown up on WinRT, WPF, Windows Forms,
Windows Phone, MonoDroid, Monogame, and ASP.NET.

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

 

 

还有贰个姐妹篇

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图