Control Not yielding after Task.Result in a non As

2019-08-24 13:43发布

问题:

This question already has an answer here:

  • await works but calling task.Result hangs/deadlocks 5 answers

I was experimenting with an MSDN sample. I came across the following issue. When running an async block of code from a button click event handler, with async pattern, it works fine (button1_Click). But for button2_Click, the control is not going beyond TaskObj.Result. What might be the reason for the same?

  private async void button1_Click(object sender, EventArgs e)//this works fine
    {
        Task<int> TaskObj = AccessTheWebAsync();
        string a ="I came here..";

        int y = await TaskObj;

        string a1 = "I came here also..";
    }

    private  void button2_Click(object sender, EventArgs e)//this is not working
    {
        Task<int> TaskObj = AccessTheWebAsync();
        string a = "I came here..";//control yielded here
        int y = TaskObj.Result;//something happened here
        string a1 = "Why I couldn't come here?";//why control is not reaching here?
    }


    async Task<int> AccessTheWebAsync()
    { 
        HttpClient client = new HttpClient();
        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");        
        DoIndependentWork();
        string urlContents = await getStringTask;
        return urlContents.Length;
    }

    private void DoIndependentWork()
    {
      IList<Int32>intK = Enumerable.Range(1, 100000000).ToList();
      foreach (int x in intK)
      {
          int y = x * 2;
      }
    }

回答1:

What might be the reason for the same?

The difference is one deadlocks, while the other doesn't. When you await, you asynchronously wait by yielding control back to the calling method. When you use Task.Result, you synchronously block on the call. This leads to a deadlock.

Why? because there is something called a "Synchronization Context" involved here, which is responsible for some of the magic of executing your continuation (all the code after the first await) inside the same context it previously used, which in your case is the UI thread. Because you block synchronously with Task.Result, the continuation is unable to marshal itself back onto the UI thread, since it is waiting on itself at the .Result.

Instead, use await as with your previous button click:

private async void button2_Click(object sender, EventArgs e)//this is not working
{
    Task<int> TaskObj = AccessTheWebAsync();
    string a = "I came here..";//control yielded here
    int y = await TaskObj;
}