2010-07-19 6 views
21

Я читаю книгу о параллельной библиотеке задач C# и имею следующий пример, но обработчик TaskScheduler.UnobservedTaskException никогда не запускается. Может ли кто-нибудь дать мне какие-либо подсказки о том, почему?TaskScheduler.UnobservedTaskException обработчик события никогда не запускается

TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
{ 
    eventArgs.SetObserved(); 
    ((AggregateException)eventArgs.Exception).Handle(ex => 
    { 
     Console.WriteLine("Exception type: {0}", ex.GetType()); 
     return true; 
    }); 
}; 

Task task1 = new Task(() => 
{ 
    throw new ArgumentNullException(); 
}); 

Task task2 = new Task(() => { 
    throw new ArgumentOutOfRangeException(); 
}); 

task1.Start(); 
task2.Start(); 

while (!task1.IsCompleted || !task2.IsCompleted) 
{ 
    Thread.Sleep(5000); 
} 

Console.WriteLine("done"); 
Console.ReadLine(); 
+3

Что это за книга? –

+0

Мне было бы очень любопытно, например, пример неверен, поскольку в этом примере невозможно создать мероприятие ... –

+1

Это книга: http://www.apress.com/book/view/1430229675 Pro .NET 4 Параллельное программирование на C# – devlife

ответ

34

К сожалению, этот пример никогда не покажет вам ваш код. UnobservedTaskException произойдет только в том случае, если задача будет собрана GC без исключения - если вы держите ссылку на task1 и task2, GC никогда не будет собираться, и вы никогда не увидите обработчик исключений.

Чтобы увидеть поведение UnobservedTaskException в действии, я хотел бы попробовать следующее (надуманный пример):

public static void Main() 
{ 
    TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
       { 
        eventArgs.SetObserved(); 
        ((AggregateException)eventArgs.Exception).Handle(ex => 
        { 
         Console.WriteLine("Exception type: {0}", ex.GetType()); 
         return true; 
        }); 
       }; 

    Task.Factory.StartNew(() => 
    { 
     throw new ArgumentNullException(); 
    }); 

    Task.Factory.StartNew(() => 
    { 
     throw new ArgumentOutOfRangeException(); 
    }); 


    Thread.Sleep(100); 
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 

    Console.WriteLine("Done"); 
    Console.ReadKey(); 
} 

Это покажет вам ваши сообщения. Первый вызов Thread.Sleep(100) обеспечивает достаточное время для выполнения задач. Сбор и ожидание приводит к сбору коллекции GC, которая активирует ваш обработчик событий 2x.

+0

Это объясняет это. Благодаря! – devlife

+6

В .NET 4.5 вам нужно дополнительное вуду, чтобы это произошло. Во-первых, вам нужно включить [ThrowUnobservedTaskException] (http://msdn.microsoft.com/en-GB/library/jj160346.aspx) в app.config, а во-вторых, вам нужно создать код в конфигурации Release (точка, которая только задокументирована в разделе «Пример» связанной статьи MSDN). Я виню это «давайте подавляем исключения молча» в JavaScript в браузерах :) –

+2

@romkyns, мне не нужно было это делать для моего приложения .NET 4.5. –

3

Исключение не будет «незаметным» в этом фрагменте образца. Пока сборщик мусора не избавится от экземпляров Task. Вам придется переписать его вот так:

class Program { 
    static void Main(string[] args) { 

     TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
     { 
      eventArgs.SetObserved(); 
      ((AggregateException)eventArgs.Exception).Handle(ex => 
      { 
       Console.WriteLine("Exception type: {0}", ex.GetType()); 
       return true; 
      }); 
     }; 

     Run(); 

     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 

     Console.WriteLine("done"); 
     Console.ReadLine(); 
    } 

    static void Run() { 
     Task task1 = new Task(() => { 
      throw new ArgumentNullException(); 
     }); 

     Task task2 = new Task(() => { 
      throw new ArgumentOutOfRangeException(); 
     }); 

     task1.Start(); 
     task2.Start(); 

     while (!task1.IsCompleted || !task2.IsCompleted) { 
      Thread.Sleep(50); 
     } 
    } 
} 

Не делайте этого, используйте Task.Wait().

0

я запускаю этот код на .NET 4.5, Visual Studio 2012 (Debug или Release, не имеет значения), я не поставил ThrowUnobservedTaskException в моем app.config:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Test 
{ 
    public static class Program 
    { 
     private static void Main() 
     { 
      TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 

      RunTask(); 

      Thread.Sleep(2000); 

      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      GC.Collect(); 

      Console.ReadLine(); 
     } 

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

     private static void RunTask() 
     { 
      Task<int> task = Task.Factory.StartNew(() => 
      { 
       Thread.Sleep(1000); // emulate some calculation 
       Console.WriteLine("Before exception"); 
       throw new Exception(); 
       return 1; 
      }); 
     } 
    } 
} 

И за исключением улавливается обработчиком UnobservedTaskException («Caught!»).

+0

Это потому, что 'ThrowUnobservedTaskException 'необходимо только в том случае, если вы хотите вернуться к поведению 4.0 исключения задачи, заканчивающему процесс. В 4.5 значение по умолчанию было изменено на * not *, завершение процесса. Для незаметного исключения в вашем примере не требуется, чтобы обработчик события был пойман. Дополнительная информация в примечаниях [здесь] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.unobservedtaskexception% 28v = vs.110% 29.aspx) – BitMask777