Ошибка бросают дважды Task.Wait после тайм-аута ожиданияC#/Задачи Ошибка после входа дважды Task.Wait после ожидания таймаута
Эта проблема, кажется, проистекает из того, что я использую Task.Wait для тайм-аут. Проблема заключается в том, что либо исключение, отправленное после таймаута задачи, регистрируется дважды, либо ошибка, возникшая до того, как таймаут не заносится в журнал. Я добавил код и тест, которые я использовал, чтобы лучше понять сценарий.
Идея этого теста заключается в том, что мы вынуждаем тайм-аут (через 2 секунды) ПРЕЖДЕ ЧЕМ исключить исключение (через 3 секунды). Что происходит с исключением в этой ситуации? Ниже приведен результат. Исключение «бум» никогда не сообщается. Он остается как незаметное исключение в Задаче.
[MassUpdateEngine.cs]
// Package the collection of statements that need to be run as a Task.
// The Task can then be given a cancellation token and a timeout.
Task task = Task.Run(async() =>
{
try
{
Thread.Sleep(3000);
throw new Exception("boom");
// Checking whether the task was cancelled at each step in the task gives us finer grained control over when we should bail out.
token.ThrowIfCancellationRequested();
Guid id = SubmitPreview();
results.JobId = id;
token.ThrowIfCancellationRequested();
bool previewStatus = await GetPreviewStatus(id, token);
Logger.Log("Preview status: " + previewStatus);
token.ThrowIfCancellationRequested();
ExecuteUpdate(id);
token.ThrowIfCancellationRequested();
bool updateStatus = await GetUpdateStatus(id, token);
Logger.Log("Update status: " + updateStatus);
token.ThrowIfCancellationRequested();
string value = GetUpdateResults(id);
results.NewValue = value;
}
// It appears that awaited methods will throw exceptions on when cancelled.
catch (OperationCanceledException)
{
Logger.Log("***An operation was cancelled.***");
}
}, token);
task.ContinueWith(antecedent =>
{
//Logger.Log(antecedent.Exception.ToString());
throw new CustomException();
}, TaskContinuationOptions.OnlyOnFaulted);
[Program.cs]
try
{
MassUpdateEngine engine = new MassUpdateEngine();
// This call simulates calling the MassUpdate.Execute method that will handle preview + update all in one "synchronous" call.
//Results results = engine.Execute();
// This call simulates calling the MassUpdate.Execute method that will handle preview + update all in one "synchronous" call along with a timeout value.
// Note: PreviewProcessor and UpdateProcessor both sleep for 3 seconds each. The timeout needs to be > 6 seconds for the call to complete successfully.
int timeout = 2000;
Results results = engine.Execute(timeout);
Logger.Log("Results: " + results.NewValue);
}
catch (TimeoutException ex)
{
Logger.Log("***Timeout occurred.***");
}
catch (AggregateException ex)
{
Logger.Log("***Aggregate exception occurred.***\n" + ex.ToString());
}
catch (CustomException ex)
{
Logger.Log("A custom exception was caught and handled.\n" + ex.ToString());
}
Поскольку исключение не соблюдается и, следовательно, не регистрируется надлежащим образом, это не сработает.
Эта информация приводит к следующим правилам:
- Не бросайте от .ContinueWith. Исключения, отброшенные отсюда, не перенаправляются обратно в вызывающий поток. Эти исключения остаются незаметными исключениями и эффективно употребляются в пищу.
- Исключение из задачи может быть или не быть отправлено обратно в вызывающий поток при использовании Wait с таймаутом. Если это исключение происходит до истечения времени ожидания вызова Wait, то исключение будет перенаправлено обратно на вызывающий поток. Если возникает исключение ПОСЛЕ таймаута для вызова Wait, исключение остается как незаметное исключение задачи.
Правило № 2 довольно уродливое. Как мы надежно регистрируем исключения в этом сценарии? Мы могли бы использовать .ContinueWith/OnlyOnFaulted для регистрации исключения (см. Ниже).
task.ContinueWith(antecedent =>
{
Logger.Log(antecedent.Exception.ToString());
//throw new CustomException();
}, TaskContinuationOptions.OnlyOnFaulted);
Однако, если исключение происходит до истечения времени ожидания для вызова Wait, то исключение будет получить ранжировано обратно вызывающую нить и обрабатывается глобальным необработанным обработчиком исключений (и запротоколировано), а затем будет передано Задача .ContinueWith (и зарегистрирована), в результате чего две записи журнала ошибок для одного и того же исключения.
Должно быть что-то, что мне здесь не хватает. Любая помощь будет оценена по достоинству.