码迷,mamicode.com
首页 > 其他好文 > 详细

Async/Await Context 与 ConfigureAwait

时间:2014-10-28 00:35:38      阅读:237      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   io   color   os   ar   sp   div   

Context

当你await一个awaitable对象的时候,编译器会捕捉当前的context,并且在执行await之后的代码时应用这个context。

那么,context具体是什么呢?

简单地说:

1. 如果你在一个UI线程上,那么context就是一个UI context

2. 如果你在相应ASP.NET Request的线程上,那么它就是一个ASP.NET request context

3. 否则,它就是一个thread pool context

更加准确点说

1. 如果SynchronizationContext.Current不是null,那么context就是SynchronizationContext.Current

2. 否则,context是当前的TaskScheduler

为什么要这样设计呢?一个很重要的原因是为了让UI/ASP.NET的异步编程变得更加容易,透明。

private async void DownloadFileButton_Click(object sender, EventArgs e)
{
  // Since we asynchronously wait, the UI thread is not blocked by the file download.
  await DownloadFileAsync(fileNameTextBox.Text);

  // Since we resume on the UI context, we can directly access UI elements.
  resultTextBox.Text = "File downloaded!";
}

Avoid Context

很多时候,我们希望编译器不要把context保存下来,并且在await之后恢复。这样做有两个原因,1. 提高效率 2. 避免死锁

提高效率

通常一个await之后,可能会有另外一个或者多个await,或者await之后,没有修改UI等操作,因此也就不需要必须回到MAIN THREADS上去。

private async Task DownloadFileAsync(string fileName)
{
  // Use HttpClient or whatever to download the file contents.
  var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false);

  // Note that because of the ConfigureAwait(false), we are not on the original context here.
  // Instead, we‘re running on the thread pool.

  // Write the file contents out to a disk file.
  await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false);

  // The second call to ConfigureAwait(false) is not *required*, but it is Good Practice.
}

避免死锁

private void Button_Click(object sender, RoutedEventArgs e)
{
    string result = GetPageStatus().Result;
    Textbox.Text = result;
}
public async Task<string> GetPageStatus()
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync("http://www.google.com");
        return response.StatusCode.ToString();
    }
}

这段代码看起来很正常,但是会死锁。

为什么呢

1. GetPageStatus.Result会开始async的Task,然后await HttpClient返回。这时候,执行context,即UI context会被保存下来。等到HttpClient返回之后,执行response.StatusCode.ToString()时恢复这个context。

2. 同时,.Result 操作会阻塞当前线程,即UI线程,等到GetPageStatus执行完成并且返回,读取结果。

3. 个么问题来了,当HttpClient返回之后,编译器期望把第一步保存的context,即UI context拿过来,执行response.StatusCode.ToString(),但是UI线程被block住了,必须等待GetPageStatus执行结束之后才可用。这就形成了死锁。

要解决这个问题也很简单。

我们可以不用.Result,而是用await。这才是提倡的做法

private async void Button_Click(object sender, RoutedEventArgs e)
{
     string result = await GetPageStatus();
     Textbox.Text = result;
}
public async Task<string> GetPageStatus()
{
     using (var httpClient = new HttpClient())
     {
        var response = await httpClient.GetAsync("http://www.google.com");
        return response.StatusCode.ToString();
     }
}

如果非要用.Result呢,那也是有办法的。就是用上面提到的ConfigureAwait,不让编译器保存context

private void Button_Click(object sender, RoutedEventArgs e)
{
    string result = GetPageStatus().Result;
    Textbox.Text = result;
}
public async Task<string> GetPageStatus()
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync("http://www.google.com").ConfigureAwait(false);
        return response.StatusCode.ToString();
    }
}

 

Async/Await Context 与 ConfigureAwait

标签:style   blog   http   io   color   os   ar   sp   div   

原文地址:http://www.cnblogs.com/wangguangxin/p/4055447.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!