2011-12-30 24 views
23

Tôi có một phương pháp tạo một số Công việc và sau đó đợi chúng với WaitAll trước khi trở về. Vấn đề là, nếu những nhiệm vụ đó bị hủy, thì WaitAll sẽ ném một số AggregateException chứa rất nhiều TaskCanceledException s.Làm cách nào để tôi có thể đợi các tác vụ mà không cần ném TaskCanceledExceptions?

Điều đó có nghĩa rằng WaitAll sẽ ném ngoại lệ trong hai hoàn cảnh khác nhau:

  • Exceptions mà chỉ ra một lỗi chính hãng. Điều này có nghĩa là có một điều kiện chúng tôi không biết cách xử lý; họ cần tuyên truyền là ngoại lệ chưa được giải quyết, cho đến khi họ cuối cùng chấm dứt quá trình.
  • Trường hợp ngoại lệ cho biết rằng người dùng đã nhấp vào nút Hủy. Điều này có nghĩa là nhiệm vụ đã bị hủy và được dọn dẹp, và chương trình sẽ tiếp tục chạy bình thường.

Loại sau phù hợp với định nghĩa của vexing exception: đó là ngoại lệ được ném trong trường hợp hoàn toàn không ngoại lệ, vì vậy tôi phải nắm bắt nó để tiếp tục luồng kiểm soát bình thường. May mắn thay nó dễ nắm bắt, phải không? Chỉ cần thêm catch (AggregateException) và - ồ chờ đợi, đó là cùng loại được đưa ra khi có lỗi nghiêm trọng.

Tôi cần phải chờ cho các tác vụ kết thúc chạy trước khi tôi trở lại (tôi cần biết rằng họ không còn sử dụng kết nối cơ sở dữ liệu, xử lý tệp hoặc bất kỳ thứ gì khác), vì vậy tôi cần WaitAll hoặc giống. Và nếu có bất kỳ nhiệm vụ nào bị lỗi, tôi muốn những ngoại lệ đó lan truyền như những ngoại lệ chưa được giải quyết. Tôi chỉ không muốn ngoại lệ để hủy bỏ.

Làm cách nào để ngăn chặn WaitAll từ việc ném ngoại lệ cho các tác vụ bị hủy?

+0

Sử dụng quá tải mà phải mất một CancellationToken. –

+0

@HansPassant, các tài liệu cho quá tải đó không thực sự nói những gì nó sử dụng mã thông báo cho. Nó sẽ bỏ qua TaskCanceledExceptions liên kết với mã thông báo đó, hoặc nó sẽ chỉ trở lại sớm nếu mã thông báo bị hủy bỏ? Tôi cần nó không trở lại cho đến khi tất cả các nhiệm vụ đã ngừng chạy. –

+1

Bạn sử dụng CancellationToken để hủy các tác vụ. Và lọc các OperationCancelException cụ thể bạn sẽ nhận được. –

Trả lời

28

AggregateException cung cấp phương thức Handle có thể được sử dụng cho các tình huống này. Nếu ví dụ bạn muốn bỏ qua TaskCanceledException bạn có thể làm:

var all = new AggregateException(
    new NullReferenceException(), 
    new TaskCanceledException(), 
    new TaskCanceledException(), 
    new InvalidOperationException(), 
    new TaskCanceledException()); 

try 
{ 
    throw all; 
} 
catch (AggregateException errors) 
{ 
    errors.Handle(e => e is TaskCanceledException); 
} 

Nếu tất cả các trường hợp ngoại lệ là loại TaskCanceledException, phương pháp Handle sẽ không ném bất kỳ ngoại lệ; nếu không, AggregateException mới chỉ chứa các ngoại lệ chưa được giải quyết sẽ bị ném.

1

Dựa trên João Angelo's suggestion, ở đây đi một lớp mở rộng công tác

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace MySharedLibrary.Extensions 
{ 
    public static class TaskExtensions 
    { 

     // This code is based João Angelo's stackoverflow suggestion https://stackoverflow.com/a/8681687/378115 

     // Use this when a CancellationTokenSource is used 
     public static void SafeWait(this Task TargetTask, CancellationTokenSource TargetTaskCancellationTokenSource) 
     { 
      if (TargetTaskCancellationTokenSource.IsCancellationRequested == false) 
      { 
       TargetTaskCancellationTokenSource.Cancel(); 
      } 
      SafeWait(TargetTask); 
     } 

     // Use this when no CancellationTokenSource is used 
     public static void SafeWait(this Task TargetTask) 
     { 
      try 
      { 
       if (TargetTask.IsCanceled == false) 
       { 
        TargetTask.Wait(); 
       } 
      } 
      catch (AggregateException errors) 
      { 
       errors.Handle(e => e is TaskCanceledException); 
      } 
     } 

    } 
}