2010-10-19 35 views
6

Dựa trên nghiên cứu của tôi, tôi đã học được những điều sau đây:TaskScheduler.UnobservedTaskException không bao giờ được gọi

  1. TaskScheduler.UnobservedTaskException phải đợi cho nhiệm vụ được thu gom rác thải trước khi ngoại lệ không quan sát được của nhiệm vụ đó sẽ bong bóng lên đến sự kiện UnobservedTaskException.
  2. Nếu bạn đang sử dụng Task.Wait(), nó sẽ không bao giờ được gọi, bởi vì bạn đang chặn kết quả sắp xảy ra từ Tác vụ, do đó ngoại lệ sẽ được ném trên Task.Wait() thay vì bong bóng lên đến sự kiện UnobservedException.
  3. Gọi GC.Collect() theo cách thủ công thường là ý tưởng tồi trừ khi bạn biết chính xác mình đang làm gì, do đó tốt trong trường hợp này là xác nhận mọi thứ, nhưng không phải là giải pháp thích hợp cho vấn đề.

Vấn đề

Nếu thoát ứng dụng của tôi trước khi thu gom rác đá vào, tôi hoàn toàn 100% có thể không nhận được sự kiện UnobservedTaskException tôi để bắn.

Lưu ý đoạn mã sau:

class Program 
{ 
    static void Main(string[] args) 
    { 
     TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
     Task.Factory.StartNew(() => 
     { 
      Console.WriteLine("Task started."); 
      throw new Exception("Test Exception"); 
     }); 
     Thread.Sleep(1000); 
     //GC.Collect(); 
     //GC.WaitForPendingFinalizers(); 
    } 

    static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
    { 
     File.WriteAllText(@"C:\data\TestException.txt", e.Exception.ToString()); 
     Console.WriteLine("EXCEPTION UNOBSERVED"); 
    } 
} 

Không có tập tin ngoại lệ được viết và không có gì được ghi vào giao diện điều khiển. 10-15 phút và nhiều hơn nữa có thể đi sau khi các ứng dụng thoát và vẫn còn tôi thấy không có bằng chứng rằng phần còn lại của ứng dụng của tôi là rác thu thập được. Bạn có thể hỏi, tại sao không chỉ thu thập khi xuất cảnh? Vâng, kịch bản thế giới thực của tôi là bẫy ngoại lệ của tôi chạy bên trong một dịch vụ WCF được lưu trữ bên trong một dịch vụ Windows. Tôi không thể bẫy khi dịch vụ Windows tắt (và do đó theo cách thủ công gọi GC.Collect()) vì không có sự kiện nào cho đến mức tôi có thể thấy.

Tôi làm sai ở đâu? Làm thế nào để đảm bảo rằng nếu một cái gì đó sâu bên trong dịch vụ WCF là cuối cùng sẽ phá vỡ dịch vụ cửa sổ của tôi, rằng tôi có một cơ hội để đăng nhập ngoại lệ trước khi dịch vụ rơi hơn?

Trả lời

2

Nathan,

Tất cả các điểm đều đúng. Hãy thử như sau:

namespace ConsoleApplication1 
{ 
    using System; 
    using System.Threading; 
    using System.Threading.Tasks; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
      Task.Factory.StartNew(() => 
      { 
       Console.WriteLine("Task started."); 
       throw new Exception("Test Exception"); 
      }); 

      Thread.Sleep(1000); 
      Console.WriteLine("First Collect"); 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      Console.WriteLine("Waiting"); 
      Console.ReadKey(); 
     } 

     static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
     { 
      Console.WriteLine("EXCEPTION UNOBSERVED"); 
     } 
    } 
} 

tôi đã nhận thấy rằng các chương trình gỡ rối thường "bẫy" các sự kiện UnobservedTaskException, làm cho nó không bắn một cách thích hợp. Chạy bên ngoài trình gỡ lỗi và nó sẽ in "EXCEPTION UNOBSERVED" mỗi lần trước khi tắt.

+0

Xin chào, cảm ơn đề xuất, mặc dù tôi thực sự đã chạy nó bên ngoài trình gỡ lỗi.Tôi cũng nhận thấy bạn đang sử dụng GC.Collect(). Điều gì xảy ra trong ví dụ của bạn nếu bạn không tự thu thập? –

+0

@Nathan: Thông thường, không có đủ áp lực GC để bao giờ có điều này xảy ra trong ví dụ đơn giản này. Nếu GC không thu thập, "nhiệm vụ" sẽ không nhấn finalizer, và UnobservedException không bao giờ cháy. Nó chỉ xảy ra khi finalizer thu thập một nhiệm vụ unrooted với một ngoại lệ. –

+0

@Nathan: Lưu ý rằng UnobservedTaskException sẽ không ngăn ứng dụng tắt trong mọi trường hợp - bạn cần xử lý các ngoại lệ bên trong nhiệm vụ của mình hoặc luôn chờ nhiệm vụ, nếu bạn muốn ngăn điều này tắt ứng dụng. –

5

Với tôi, TaskScheduler.UnobservedTaskException lúc đầu cho cảm giác bảo mật rất sai. Nó thực sự không có giá trị nhiều nếu nó phụ thuộc vào Bộ sưu tập rác.

Tôi đã tìm thấy giải pháp sau đây, lấy từ this msdn article, đáng tin cậy hơn nhiều. Về cơ bản nó thực thi khối tiếp tục (nơi bạn đăng nhập ngoại lệ) chỉ khi có các ngoại lệ không được giải quyết trong task1, và không chặn thực thi UI.

Bạn cũng có thể muốn làm phẳng các tham số AggregateExested lồng nhau và có thể tạo phương thức mở rộng, như Reed Copsey depicted here.

var task1 = Task.Factory.StartNew(() => 
{ 
    throw new MyCustomException("Task1 faulted."); 
}) 
.ContinueWith((t) => 
{ 
    Console.WriteLine("I have observed a {0}", 
     t.Exception.InnerException.GetType().Name); 
}, 
TaskContinuationOptions.OnlyOnFaulted); 
Các vấn đề liên quan