Использование задач с условными продолжениями
Я немного смущен тем, как использовать Задачи с условными продолжениями.
Если у меня есть задача, а затем я хочу продолжить выполнение задач, которые обрабатывают успех и ошибку, а затем дождаться завершения.
void FunctionThrows() {throw new Exception("faulted");}
static void MyTest()
{
var taskThrows = Task.Factory.StartNew(() => FunctionThrows());
var onSuccess = taskThrows.ContinueWith(
prev => Console.WriteLine("success"),
TaskContinuationOptions.OnlyOnRanToCompleted);
var onError = taskThrows.ContinueWith(
prev => Console.WriteLine(prev.Exception),
TaskContinuationOptions.OnlyOnFaulted);
//so far, so good
//this throws because onSuccess was cancelled before it was started
Task.WaitAll(onSuccess, onError);
}
Является ли это предпочтительным способом выполнения разветвления задачи/отказа задачи? Кроме того, как я должен присоединиться ко всем этим задачам, предположим, что я создал длинную строку продолжений, каждая из которых имеет собственную обработку ошибок.
//for example
var task1 = Task.Factory.StartNew(() => ...)
var task1Error = task1.ContinueWith( //on faulted
var task2 = task1.ContinueWith( //on success
var task2Error = task2.ContinueWith( //on faulted
var task3 = task2.ContinueWith( //on success
//etc
Вызов WaitAll
для этих неизменно бросает, потому что некоторые из продолжений будут отменены из-за TaskContinuationOptions
и вызывают Wait
на отмененных задачах.
Как мне присоединиться к ним, не получив "Отменить задачу"? "
Ответы
Ответ 1
Я думаю, что ваша основная проблема заключается в том, что вы сообщаете этим двум задачам "Подождите" своим вызовом
Task.WaitAll(onSuccess, onError);
Продолжения onSuccess и onError автоматически настраиваются для вас и будут выполняться после завершения антецедентной задачи.
Если вы просто замените Task.WaitAll(...)
на taskThrows.Start();
, я верю, что вы получите желаемый результат.
Вот несколько примеров, которые я собрал:
class Program
{
static int DivideBy(int divisor)
{
Thread.Sleep(2000);
return 10 / divisor;
}
static void Main(string[] args)
{
const int value = 0;
var exceptionTask = new Task<int>(() => DivideBy(value));
exceptionTask.ContinueWith(result => Console.WriteLine("Faulted ..."), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
exceptionTask.ContinueWith(result => Console.WriteLine("Success ..."), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
exceptionTask.Start();
try
{
exceptionTask.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine("Exception: {0}", ex.InnerException.Message);
}
Console.WriteLine("Press <Enter> to continue ...");
Console.ReadLine();
}
}
Ответ 2
Разве это не нормально?
Глядя на документацию MSDN, вы делаете это хорошо, и логика, которую вы реализуете, звучит. Единственное, чего вам не хватает, это обернуть вызов WaitAll в обертке AggregateException следующим образом:
// Exceptions thrown by tasks will be propagated to the main thread
// while it waits for the tasks. The actual exceptions will be wrapped in AggregateException.
try
{
// Wait for all the tasks to finish.
Task.WaitAll(tasks);
// We should never get to this point
Console.WriteLine("WaitAll() has not thrown exceptions. THIS WAS NOT EXPECTED.");
}
catch (AggregateException e)
{
Console.WriteLine("\nThe following exceptions have been thrown by WaitAll(): (THIS WAS EXPECTED)");
for (int j = 0; j < e.InnerExceptions.Count; j++)
{
Console.WriteLine("\n-------------------------------------------------\n{0}", e.InnerExceptions[j].ToString());
}
}
Вы можете прочитать больше здесь:
http://msdn.microsoft.com/en-us/library/dd270695.aspx
В сущности, улавливание AggregatedException дает вам то же самое, что и завершение WaitAll. Это сборник всех исключений, возвращаемых из ваших задач.
Ответ 3
Используйте Task.WaitAny(onSuccess, onError);