11

Tôi muốn đặt mã đầu tiên và sau đó giải thích tình hình và yêu cầu câu hỏi của tôi dựa trên rằng:async/chờ đợi với tham số continueOnCapturedContext ConfigureAwait và SynchronizationContext cho continuations không đồng bộ

public partial class MainWindow : Window { 

    public MainWindow() { 
     InitializeComponent(); 
    } 

    private async void Button_Click_2(object sender, RoutedEventArgs e) { 

     var result = await GetValuesAsync(); 
     Foo.Text += result; 
    } 

    public async Task<string> GetValuesAsync() {   

     using (var httpClient = new HttpClient()) { 

      var response = await httpClient 
       .GetAsync("http://www.google.com") 
       .ConfigureAwait(continueOnCapturedContext: false); 


      // This is the continuation for the httpClient.GetAsync method. 
      // We shouldn't get back to sync context here 
      // Cuz the continueOnCapturedContext is set to *false* 
      // for the Task which is returned from httpClient.GetAsync method 
      var html = await GetStringAsync(); 

      // This is the continuation for the GetStringAsync method. 
      // Should I get back to sync context here? 
      // Cuz the continueOnCapturedContext is set to *true* 
      // for the Task which is returned from GetStringAsync 

      // However, GetStringAsync may be executed in another thread 
      // which has no knowledge for the sync context 
      // because the continueOnCapturedContext is set to *false* 
      // for the Task which is returned from httpClient.GetAsync method. 

      // But, on the other hand, GetStringAsync method also has a 
      // chance to be executed in the UI thread but we shouldn't be 
      // relying on that. 
      html += "Hey..."; 
      Foo.Text = html; 

      return html; 
     } 
    } 

    public async Task<string> GetStringAsync() { 

     await Task.Delay(1000); 
     return "Done..."; 
    } 
} 

Đây là một mẫu WPF khá đơn giản mà chạy trên .NET 4.5 và có lẽ không có nhiều ý nghĩa nhưng điều này sẽ giúp tôi giải thích tình hình của tôi.

Tôi có một nút trên màn hình có sự kiện nhấp không đồng bộ. Khi bạn nhìn vào mã GetValuesAsync, bạn sẽ thấy việc sử dụng từ khóa await hai lần. Với lần sử dụng đầu tiên, tôi đặt thông số continueOnCapturedContext của phương thức Task.ConfigureAwait thành false. Vì vậy, điều này chỉ ra rằng tôi không nhất thiết muốn tiếp tục của tôi được thực thi bên trong SynchronizationContext.Current. Càng xa càng tốt.

Ở lần sử dụng thứ hai await (với phương thức GetStringAsync), tôi không gọi phương thức ConfigureAwait. Vì vậy, về cơ bản tôi đã chỉ ra rằng tôi muốn để quay lại ngữ cảnh đồng bộ hóa hiện tại để tiếp tục phương thức GetStringAsync. Vì vậy, như bạn có thể thấy, tôi cố gắng đặt thuộc tính TextBlock.Text (thuộc về chủ đề giao diện người dùng) bên trong phần tiếp theo.

Khi tôi chạy ứng dụng và nhấp vào nút, tôi nhận được một ngoại lệ cho tôi được thông báo sau:

Các thread gọi không thể truy cập đối tượng này vì một khác nhau chủ đề sở hữu nó.

Lúc đầu, điều này không có ý nghĩa với tôi và tôi nghĩ rằng tôi đã phát hiện ra một lỗi nhưng sau đó, tôi nhận ra rằng GetStringAsync có thể được thực hiện trong một thread (rất có khả năng) là khác nhau với thread UI và không có kiến thức về ngữ cảnh đồng bộ hóa vì continueOnCapturedContext được đặt thành false cho số Task được trả về từ phương thức httpClient.GetAsync.

Đây có phải là trường hợp ở đây không? Ngoài ra, trong trường hợp này, có một cơ hội cho phương thức GetStringAsync được đăng lại cho chủ đề UI bacuse việc tiếp tục phương pháp httpClient.GetAsync có thể được thực thi bên trong chuỗi giao diện người dùng không?

Tôi cũng có một vài nhận xét bên trong mã. Theo quan điểm của các câu hỏi của tôi và các ý kiến ​​bên trong mã, tôi có thiếu gì ở đây không?

+0

Câu trả lời này có hữu ích không? http://stackoverflow.com/a/12357113/171121 –

Trả lời

11

Khi bạn gọi ConfigureAwait(false), phần còn lại của phương pháp này sẽ được thực hiện trên một sợi bơi thread trừ các Task bạn await ing là đã hoàn tất.

GetAsync gần như chắc chắn sẽ chạy không đồng bộ, tôi mong đợi GetStringAsync để chạy trên chuỗi chủ đề chuỗi.

public async Task<string> GetValuesAsync() {   

    using (var httpClient = new HttpClient()) { 

     var response = await httpClient 
      .GetAsync("http://www.google.com") 
      .ConfigureAwait(continueOnCapturedContext: false); 

     // And now we're on the thread pool thread. 

     // This "await" will capture the current SynchronizationContext... 
     var html = await GetStringAsync(); 
     // ... and resume it here. 

     // But it's not the UI SynchronizationContext. 
     // It's the ThreadPool SynchronizationContext. 
     // So we're back on a thread pool thread here. 

     // So this will raise an exception. 
     html += "Hey..."; 
     Foo.Text = html; 

     return html; 
    } 
} 

Ngoài ra, trong trường hợp này, là có một cơ hội cho phương pháp GetStringAsync được niêm yết trở lại thread UI bacuse phương pháp tiếp httpClient.GetAsync có thể được thực hiện bên trong thread UI?

Cách duy nhất GetStringAsync sẽ chạy trên thread UI là nếu GetAsync hoàn tất trước khi nó thực sự await ed. Rất khó.

Vì lý do này, tôi thích sử dụng ConfigureAwait(false) cho mỗiawait sau khi ngữ cảnh này không còn cần thiết nữa.

+0

Cảm ơn! Câu cuối cùng của bạn là cách tôi xem xét sau đây (tôi đã nhưng bây giờ tôi sẽ nhấn mạnh thêm về điều đó). điều này sẽ không tạo ra sự khác biệt đáng chú ý nhưng việc sử dụng 'ConfigureAwait (false)' cũng sẽ lưu chúng ta khỏi các kiểm tra 'SynchronizationContext' không cần thiết nếu nó không cần thiết. – tugberk

+0

"Rất khó" là không đủ, tất nhiên. Hoặc là phải 100% hoặc mã của bạn cần phải chịu đựng cả hai trường hợp. – usr

+1

Tôi đồng ý nói chung. Đó là lý do tại sao tôi thích sử dụng 'ConfigureAwait (false)' cho mọi 'await' một khi bối cảnh không còn cần thiết nữa. –

Các vấn đề liên quan