Следующее предполагает .NET 4.0 и использование TaskScheduler по умолчанию.
Прежде всего, обратите внимание, что исключения вызываются внутри делегатов, которых вы проходите, поэтому они вызываются в другом потоке, а не в том, который вы (логически) выполняете в catch
. Так или иначе, исключение должно быть передано из потока, который выполняет ваш делегатский / лямбда-код, в тот, который запустил поток / задачу.
Обратите внимание, что для Task
я думаю, что библиотека также может выбрать не запускать ее в своем собственном потоке, а в вызывающем потоке (но я не уверен, что это верно только для Parallel.ForEach
, и т. д., а не для "голого" Task
объекта).
Для того чтобы это произошло, должны быть выполнены две вещи:
- Вызывающий поток все еще активен. Иначе не останется ничего, что могло бы фактически выполнить улов.
- Исключение, которое было вызвано в потоке / задаче, должно как-то сохраняться и повторно вызываться, чтобы вы могли его перехватить.
С учетом вышесказанного оба ваших примера не ждут завершения потока / задачи. В первом примере вам не хватает task.Wait()
(или аналогичного), а во втором thread.Join()
. В зависимости от поведения времени выполнения тестовых кодов это может означать, что вы никогда не сможете наблюдать исключение из потока / задачи (пункт 1 выше).
Даже если вы добавите два вызова, это то, что происходит со мной (снова .NET 4.0):
Пример задачи : вызов task.Wait()
фактически вызывает реанимацию исключения, первоначально оставленного необработанным в делегате задачи (это то, что TPL сделает для вас внутри), он переносит его внутрь System.AggregateException
, который вы могли бы / увидели, если бы использовали что-то более точное, чем плоское «универсальное».
Тема пример: исключение, выдвинутое делегатом, остается необработанным, и ваше приложение закрывается (если вы не сделаете что-либо для необработанных исключений иначе )
Другими словами, у меня были бы следующие примеры:
// Thread example
var thread = new Thread(() => { throw null; });
thread.Start();
thread.Join();
// Should never reach here, depending on timing sometimes not even
// until the Join() call. The process terminates as soon as the other
// thread runs the "throw null" code - which can logically happen somewhere
// after the "start" of the "Start()" call.
// Task example
try
{
var task = new Task(() => { throw null; });
task.Start();
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine("Exception: " + ex);
}