2015-07-06 17 views
7

Sau khi đọc this đăng một vài tháng trước, tôi đã trở thành hoang tưởng khi nhận được Result của một Task<T> và không ngừng gói tất cả các cuộc gọi của tôi đến số điện thoại ConfigureAwait(false) hoặc Task.Run. Tuy nhiên, vì một số lý do, mã sau hoàn tất thành công:Tại sao không gọi Nhiệm vụ <T> .Result bế tắc?

public static void Main(string[] args) 
{ 
    var arrays = DownloadMany(); 

    foreach (var array in arrays); 
} 

IEnumerable<byte[]> DownloadMany() 
{ 
    string[] links = { "http://google.com", "http://microsoft.com", "http://apple.com" }; 

    using (var client = new HttpClient()) 
    { 
     foreach (var uri in links) 
     { 
      Debug.WriteLine("Still here!"); 
      yield return client.GetByteArrayAsync(uri).Result; // Why doesn't this deadlock? 
     } 
    } 
} 

Mã in Still here! 3 lần rồi thoát. Điều này có dành riêng cho HttpClient rằng việc gọi số Result có an toàn không (như trong những người đã viết nó với số điện thoại ConfigureAwait(false))?

+0

bằng cách này .. nó đơn giản hơn nhiều để không chặn và sử dụng đang chờ đợi khi cần thiết hơn bằng cách sử dụng 'ConfigureAwait' ở khắp mọi nơi. – i3arnon

+0

@ i3arnon Đúng, nhưng tôi đang viết một API hỗ trợ cả người gọi đồng bộ và không đồng bộ. –

+1

@JamesKo: Tôi khuyên bạn nên sử dụng các phương thức không đồng bộ để hiển thị các API không đồng bộ. Tuy nhiên, nếu bạn * phải * hỗ trợ cả đồng bộ hóa và không đồng bộ (ví dụ, đối với khả năng tương thích ngược), thì bạn có thể tìm thấy [bài viết MSDN gần đây của tôi trên async] (https://msdn.microsoft.com/en-us/magazine/ mt238404.aspx) hữu ích. –

Trả lời

10

Task.Result sẽ chỉ chặn khi có một số nhất định SynchronizationContext s. Trong các ứng dụng giao diện điều khiển, không có ứng dụng nào được sắp xếp theo lịch trên ThreadPool. Cũng giống như khi bạn sử dụng ConfigureAwait(false).

Trong chuỗi giao diện người dùng, ví dụ: một chuỗi có lịch tiếp tục với chuỗi giao diện người dùng duy nhất. Nếu bạn đợi đồng bộ với Task.Result bằng cách sử dụng chuỗi giao diện người dùng, trên một tác vụ chỉ có thể hoàn thành trên chuỗi giao diện người dùng, bạn đã có một bế tắc.

Hơn nữa, bế tắc phụ thuộc vào việc triển khai GetByteArrayAsync. Bạn chỉ có thể bế tắc nếu đó là một phương thức không đồng bộ và các chế độ chờ đợi của nó không sử dụng ConfigureAwait(false).

Nếu bạn muốn bạn có thể sử dụng AsyncContext của Stephen Cleary để thêm SynchronizationContext thích hợp vào ứng dụng bảng điều khiển của bạn để kiểm tra xem mã của bạn có thể chặn trong ứng dụng giao diện người dùng (hoặc ASP.Net) hay không.


Giới thiệu về phương thức trả về của tác vụ: không phải về mặt kỹ thuật. Họ không sử dụng các từ khóa asyncawait. Họ chỉ đơn giản là trả lại một nhiệm vụ. Thông thường một wrapper trên Task.Factory.FromAsync. Vì vậy, nó có thể "an toàn" ngăn chặn chúng anyway.

+1

Điều này rất lạ. Tôi nghĩ rằng điều này chỉ áp dụng cho async/await. Không có sự tiếp tục ở đây, vì vậy tôi không hiểu những gì sẽ được lên kế hoạch trên một chủ đề khác nhau. Hơn nữa, nếu 'Kết quả' * không * chặn, như bạn đề nghị, phương thức trả về là gì? Mảng trống? –

+2

@Asad Result đang chặn. Nó không chỉ chặn một chuỗi giao diện người dùng (vì đây không phải là một ứng dụng giao diện người dùng). Nó đang chặn luồng chính. – i3arnon

+1

@ i3arnon Không rõ ràng bạn đang nói về chủ đề UI nào. Quan điểm của tôi là 'Kết quả' sẽ luôn bị chặn, bất kể bối cảnh đồng bộ hóa có sẵn. Nếu phương thức trả về một 'Task' của một cái gì đó, cái gì đó phải được giải quyết trước khi getter của' Task.Result' sẽ trả về một giá trị cho bạn. Cho dù điều này đang chạy trong một ứng dụng giao diện người dùng hoặc một ứng dụng giao diện điều khiển là không quan trọng, để hiểu biết của tôi ít nhất. –

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