Как вызвать агрегатэкспений с TPL?
-
12-10-2019 - |
Вопрос
Я пытаюсь воссоздать условия, которые вызовут это исключение:
System.AggregateException: A Task's exception(s) were not observed
either by Waiting on the Task or accessing its Exception property.
As a result, the unobserved exception was rethrown by the finalizer thread.`
Я написал эту программу, думая, что я причиняю исключение, но это не так:
using System;
using System.Threading.Tasks;
namespace SomeAsyncStuff
{
class Program
{
static void Main(string[] args)
{
Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
GC.Collect();
Console.WriteLine("completed");
}
}
}
В моем реальном приложении я использую TPL, и я не кодировал свою обработку исключений правильно. В результате я получаю это исключение. Теперь я пытаюсь воссоздать те же условия в отдельной программе, чтобы экспериментировать с ненаблюдаемыми исключениями.
Решение 5
Я ОП. Я проверил GC.WaitForpendingFinalizers (), но это не помогло воссоздать исключение. Проблема заключалась в том, что GC.Colect () был выполнен до начала задачи.
Это правильный код для воссоздания исключения:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace SomeAsyncStuff
{
class Program
{
static void Main(string[] args)
{
Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
// give some time to the task to complete
Thread.Sleep(3000);
GC.Collect();
// GC.WaitForPendingFinalizers();
Console.WriteLine("completed");
}
}
}
Другие советы
Возможно, вам придется добавить звонок в GC.WaitForpendingFinalizers () после gc.collect (), поскольку финализаторы работают в их собственном потоке.
Исключение брошено TaskExceptionHolder
Finalizer, поэтому поток финализатора должен работать до того, как это исключение будет брошено. Как указывает Джош, вы можете подождать, пока это произойдет, позвонив CG.WaitForPedingFinalizers()
.
Пожалуйста, обратите внимание, что это поведение было изменено в текущем асинхронном CTP. Я поговорил со Стивеном Тубом из команды PFX об этом в Teched Europe в начале этого года, и он указал, что они должны были изменить ее, чтобы новая асинхронная функция работала правильно. Поэтому, хотя еще слишком рано говорить о следующей версии фреймворта, это поведение вполне может быть изменено в предстоящей версии.
@Sly, хотя вы придумали рабочий ответ, я думаю, что большинству людей будет лучше обслуживать предложение об ошибке «... ожидая задачи ...». Привлечение к активности GC является признаком того, что вы знаете GC глубоко и имеете узкое место в производительности, либо вам не хватает. В моем случае это означает последний;) Вызов Startnew действительно возвращает задачу, так почему бы не использовать ее? например
Задача mytask = task.factory.startnew (() => {бросить новый nullreferenceexception ("ex");}); // Дайте некоторое время выполнению задачи
mytask.wait ();
Я действительно удивлен, что вы не пытались правильно позвонить по коду после выполнения задачи, потому что кто скажет, что любой процесс завершится до 3 секунд? Не только это, но и связывает другие процессы в течение целых 3 секунд. Я бы заменил эту реализацию с помощью метода задачи Continaiarwith () для вызова GC после завершения задачи.
Task.Factory
.StartNew(() => { throw new NullReferenceException("ex"); })
.ContinueWith(p => GC.Collect());
Если вам нужен блок до тех пор, пока он не будет завершен (для вашего примера кода, который вы использовали для отладки), вы также можете сделать Waitone после начала задачи и продолжить () сигнал сигнализации ожидания. Если вам нужно делать это в своем производственном коде, то, возможно, то, что вы пытаетесь выполнить, на самом деле синхронно, когда вам не нужно беспокоиться об использовании задачи.
Самый простой способ воссоздания ошибки - это ожидание задачи.
Task task = Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
//this is where the exception will be thrown
task.Wait();
Вызов ожидание заблокирует вызов, пока задача не завершит выполнение.