2012-10-19 36 views
52

Với System.Threading.Tasks.Task<TResult>, tôi phải quản lý các ngoại lệ có thể bị ném. Tôi đang tìm cách tốt nhất để làm điều đó. Cho đến nay, tôi đã tạo một lớp cơ sở quản lý tất cả các ngoại lệ chưa được thực hiện bên trong cuộc gọi của .ContinueWith(...)Cách tốt nhất để bắt ngoại lệ trong Công việc là gì?

Tôi tự hỏi liệu có cách nào tốt hơn để làm điều đó không. Hoặc thậm chí nếu đó là một cách hay để làm điều đó.

public class BaseClass 
{ 
    protected void ExecuteIfTaskIsNotFaulted<T>(Task<T> e, Action action) 
    { 
     if (!e.IsFaulted) { action(); } 
     else 
     { 
      Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => 
      { 
       /* I display a window explaining the error in the GUI 
       * and I log the error. 
       */ 
       this.Handle.Error(e.Exception); 
      }));    
     } 
    } 
} 

public class ChildClass : BaseClass 
{ 
    public void DoItInAThread() 
    { 
     var context = TaskScheduler.FromCurrentSynchronizationContext(); 
     Task.Factory.StartNew<StateObject>(() => this.Action()) 
        .ContinueWith(e => this.ContinuedAction(e), context); 
    } 

    private void ContinuedAction(Task<StateObject> e) 
    { 
     this.ExecuteIfTaskIsNotFaulted(e,() => 
     { 
      /* The action to execute 
      * I do stuff with e.Result 
      */ 

     });   
    } 
} 

Trả lời

68

Có hai cách bạn có thể thực hiện việc này, tùy thuộc vào phiên bản ngôn ngữ bạn đang sử dụng.

C# 5.0 và ở trên

Bạn có thể sử dụng các từ khóa asyncawait để đơn giản hóa rất nhiều này cho bạn.

asyncawait đã được giới thiệu sang ngôn ngữ để đơn giản hóa bằng cách sử dụng Task Parallel Library, ngăn cản bạn cần phải sử dụng ContinueWith và cho phép bạn tiếp tục chương trình một cách từ trên xuống.

Bởi vì điều này, bạn chỉ có thể sử dụng một khối try/catch để bắt ngoại lệ, như vậy:

try 
{ 
    // Start the task. 
    var task = Task.Factory.StartNew<StateObject>(() => { /* action */ }); 

    // Await the task. 
    await task; 
} 
catch (Exception e) 
{ 
    // Perform cleanup here. 
} 

Lưu ý rằng phương pháp đóng gói trên phải sử dụng đã từ khóa async áp dụng, do đó bạn có thể sử dụng await.

C# 4.0 và dưới

Bạn có thể xử lý ngoại lệ bằng cách sử dụng ContinueWith overload mà phải mất một giá trị từ TaskContinuationOptions enumeration, như vậy:

// Get the task. 
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ }); 

// For error handling. 
task.ContinueWith(t => { /* error handling */ }, context, 
    TaskContinuationOptions.OnlyOnFaulted); 

Các OnlyOnFaulted thành viên của TaskContinuationOptions liệt kê chỉ ra rằng việc tiếp tục nên chỉ được thực hiện nếu tác vụ tiền lệ đã ném một ngoại lệ.

Tất nhiên, bạn có thể có nhiều hơn một cuộc gọi đến ContinueWith ra khỏi tiền đề tương tự, xử lý các trường hợp không ngoại lệ:

// Get the task. 
var task = new Task<StateObject>(() => { /* action */ }); 

// For error handling. 
task.ContinueWith(t => { /* error handling */ }, context, 
    TaskContinuationOptions.OnlyOnFaulted); 

// If it succeeded. 
task.ContinueWith(t => { /* on success */ }, context, 
    TaskContinuationOptions.OnlyOnRanToCompletion); 

// Run task. 
task.Start(); 
+0

Làm cách nào bạn biết loại ngoại lệ trong phương pháp ẩn danh? Nếu tôi làm t.Exception, intellisense sẽ không phơi bày các thuộc tính innerexception, message ... etc ... – guiomie

+4

@guiomie 't' * là * ngoại lệ. – casperOne

+1

ngữ cảnh không được xác định là gì? – MonsterMMORPG

2

Bạn có thể tạo một số nhà máy tùy chỉnh công tác, mà sẽ tạo ra những công việc cần xử lý ngoại lệ xử lý được nhúng. Một cái gì đó như thế này:

using System; 
using System.Threading.Tasks; 

class FaFTaskFactory 
{ 
    public static Task StartNew(Action action) 
    { 
     return Task.Factory.StartNew(action).ContinueWith(
      c => 
      { 
       AggregateException exception = c.Exception; 

       // Your Exception Handling Code 
      }, 
      TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously 
     ).ContinueWith(
      c => 
      { 
       // Your task accomplishing Code 
      }, 
      TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously 
     ); 
    } 

    public static Task StartNew(Action action, Action<Task> exception_handler, Action<Task> completion_handler) 
    { 
     return Task.Factory.StartNew(action).ContinueWith(
      exception_handler, 
      TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously 
     ).ContinueWith(
      completion_handler, 
      TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously 
     ); 
    } 
}; 

Bạn có thể quên xử lý ngoại lệ cho Công việc được tạo từ nhà máy này trong mã khách hàng của bạn. Trong cùng một thời điểm bạn vẫn có thể chờ đợi hoàn thiện Nhiệm vụ đó hoặc sử dụng chúng trong Lửa-and-Forget phong cách:

var task1 = FaFTaskFactory.StartNew(() => { throw new NullReferenceException(); }); 
var task2 = FaFTaskFactory.StartNew(() => { throw new NullReferenceException(); }, 
             c => { Console.WriteLine("Exception!"); }, 
             c => { Console.WriteLine("Success!" ); }); 

task1.Wait(); // You can omit this 
task2.Wait(); // You can omit this 

Nhưng nếu thật mà nói tôi không thực sự chắc chắn lý do tại sao bạn muốn có mã xử lý hoàn thành. Trong mọi trường hợp, quyết định này phụ thuộc vào logic của ứng dụng của bạn.

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