2014-09-02 13 views
9

Tôi đang đọc lên thêm về async ở đây: http://msdn.microsoft.com/en-us/library/hh873173(v=vs.110).aspxExceptions Chụp về hoạt động async

Đi qua ví dụ này:

Task<bool> [] recommendations = …; 
while(recommendations.Count > 0) 
{ 
    Task<bool> recommendation = await Task.WhenAny(recommendations);  
    try 
    { 
     if (await recommendation) BuyStock(symbol); 
     break; 
    } 
    catch(WebException exc) 
    { 
     recommendations.Remove(recommendation); 
    } 
} 

Tôi tự hỏi, nếu tôi đã biểu diễn đang chờ đợi trên Task.WhenAny tại sao tôi cần phải chờ đợi một lần nữa bên trong khối thử?

Nếu tôi đã làm điều này: Task<bool> recommendation = await Task.WhenAny(recommendations); Tại sao làm điều này: if (await recommendation) BuyStock(symbol);

Trả lời

8

Đầu tiên await tồn tại để đồng bộ chờ đợi cho nhiệm vụ đầu tiên để hoàn thành (ví dụ: recommendation). Thứ hai await chỉ ở đó để trích xuất kết quả thực tế của tác vụ đã hoàn thành và ném ngoại lệ được lưu trữ trong tác vụ. (điều quan trọng cần nhớ là việc đợi tác vụ hoàn thành được tối ưu hóa và sẽ thực hiện đồng bộ).

Một tùy chọn khác để nhận kết quả sẽ sử dụng Task<T>.Result, tuy nhiên nó khác với cách xử lý ngoại lệ. await sẽ ném ngoại lệ thực tế (ví dụ: WebException) trong khi Task<T>.Result sẽ ném một số AggregateException có chứa ngoại lệ thực sự bên trong.

Task<bool> [] recommendations = …; 
while(recommendations.Count > 0) 
{ 
    Task<bool> recommendation = await Task.WhenAny(recommendations);  
    try 
    { 
     if (recommendation.Result) 
     { 
      BuyStock(symbol); 
     } 
     break; 
    } 
    catch(AggregateException exc) 
    { 
     exc = exc.Flatten(); 
     if (exc.InnerExceptions[0] is WebException) 
     { 
      recommendations.Remove(recommendation); 
     } 
     else 
     { 
      throw; 
     } 
    } 
} 

Rõ ràng chờ nhiệm vụ là đơn giản hơn và vì vậy đó là cách đề nghị thu hồi Kết quả là ra khỏi một nhiệm vụ.

+1

Điều này sẽ ném một 'AggregateException', không phải là' WebException', nếu 'Task' bị lỗi do kết quả của' WebException'. – Servy

+0

@Servy đã làm rõ điều đó. – i3arnon

+2

Bây giờ bạn đã đề cập rằng có sự khác biệt, nhưng bạn vẫn bị hỏng mã do thay đổi của bạn, bởi vì bạn đã thay đổi lỗi xử lý ngữ nghĩa theo cách mã không hỗ trợ. Điểm mấu chốt của câu trả lời của bạn, rằng bạn chỉ có thể thay đổi mã để sử dụng 'Kết quả', vốn đã sai, bởi vì bằng cách thực hiện thay đổi đó bạn đã phá vỡ mã. Có một lý do * rất tốt * rằng tác giả của mã này đã sử dụng 'await' ở đây. – Servy

4

Bạn nói đúng. Nó không phải là cần thiết. Bạn có thể thay thế nó bằng

if (recommendation.Result) 
    BuyStock(symbol); 

Cũng lưu ý rằng await sẽ không chờ đợi (sẽ không được thiết lập tiếp diễn) khi nhiệm vụ hoàn thành được đưa ra. Nó sẽ chỉ thực hiện đồng bộ trong trường hợp đó như là một tối ưu hóa. Tôi đoán tác giả tận dụng tối ưu hóa đó.

Nếu bạn hỏi tại sao tác giả viết theo cách đó, Có thể là sự thống nhất không? chỉ có anh ta biết.

1

Nếu tôi đã làm điều này: Đề xuất công việc = đang chờ tác vụ.WhenBất kỳ (đề xuất); Tại sao làm điều này: nếu (chờ đợi khuyến nghị) BuyStock (biểu tượng);

Task.WhenAny lợi nhuận một Task<Task<bool>> và bạn muốn unwrap outter Task để lấy bool kết quả. Bạn có thể làm như vậy bằng cách truy cập Task.Result tài sản của trở Task

0

câu trả lời khác đã chỉ ra rằng bạn phải await nhiệm vụ được trả về bởi await Task.WhenAll để unwrap giá trị trả về (cách khác, bạn có thể sử dụng Result tài sản).

Tuy nhiên, bạn cũng có thể thoát khỏi thử của bạn/catch (và đó là một điều tốt để tránh bắt ngoại lệ không cần thiết)

Task<bool> recommendation = await Task.WhenAny(recommendations);  
if(!recommendation.IsFaulted) 
{ 
    if (await recommendation) BuyStock(symbol); 
    break; 
} 
else 
{ 
    if(recommendation.Exception.InnerExceptions[0] is WebException) 
    { 
     recommendations.Remove(recommendation); 
    } 
    else 
    { 
     throw recommendation.Exception.InnerExceptions[0]; 
    } 
} 
+0

Bạn có chắc chắn rằng khi nhiệm vụ bị lỗi 'WhenAny' không ném ngoại lệ? –

+0

@SriramSakthivel: hoàn toàn chắc chắn. 'WhenAny' trả lại một Task có kết quả là Firs Task hoàn thành. Nhiệm vụ được hoàn thành nếu nó thành công hoặc bị lỗi. Nếu một lỗi Task, 'WhenAny'sunds ans nó cung cấp cho bạn Task hoàn thành đầu tiên (ví dụ như một lỗi). – Falanwe

+0

Không phải 'giới thiệu.Exception' có chứa' AggregateException'? – i3arnon

5

Việc sử dụng await đây tạo ra ngữ nghĩa xử lý lỗi mong muốn. Nếu anh ta sử dụng Result thay vì await thì AggregateException sẽ được khôi phục trực tiếp; khi sử dụng await ngoại lệ đầu tiên trong số AggregateException được rút ra rằng ngoại lệ được ném lại. Xóa tác giả của mã này muốn số WebException bị ném, thay vì một số AggregateException mà anh ta cần phải tự tay mở.

Anh ấy có thể đã sử dụng một cách tiếp cận khác không. Đây chỉ đơn giản là cách tiếp cận mà tác giả của mã được ưa thích, vì nó cho phép anh ta viết mã giống như mã đồng bộ truyền thống hơn là thay đổi hoàn toàn kiểu mã.