Я хочу иметь возможность запускать все Задачи параллельно, и как только вернется первое «Хорошее», отмените остальные и получите «Хорошее».
Это недоразумение, поскольку Отмена в TPL является кооперативной , поэтому после запуска Задачи отменить ее невозможно. CancellationToken
может работать до запуска Задачи или позже, чтобы вызвать исключение, если запрашивается Отмена, которая предназначена для инициирования и выполнения необходимых действий, таких как выбрасывание пользовательского исключения из логики
Проверьте следующий запрос , в нем есть много интересных ответов, но ни один из них Отмена. Ниже также возможен вариант:
public static class TaskExtension<T>
{
public static async Task<T> FirstSuccess(IEnumerable<Task<T>> tasks, T goodResult)
{
// Create a List<Task<T>>
var taskList = new List<Task<T>>(tasks);
// Placeholder for the First Completed Task
Task<T> firstCompleted = default(Task<T>);
// Looping till the Tasks are available in the List
while (taskList.Count > 0)
{
// Fetch first completed Task
var currentCompleted = await Task.WhenAny(taskList);
// Compare Condition
if (currentCompleted.Status == TaskStatus.RanToCompletion
&& currentCompleted.Result.Equals(goodResult))
{
// Assign Task and Clear List
firstCompleted = currentCompleted;
break;
}
else
// Remove the Current Task
taskList.Remove(currentCompleted);
}
return (firstCompleted != default(Task<T>)) ? firstCompleted.Result : default(T);
}
}
Использование:
var t1 = new Task<string>(()=>"bad");
var t2 = new Task<string>(()=>"bad");
var t3 = new Task<string>(()=>"good");
var t4 = new Task<string>(()=>"good");
var taskArray = new []{t1,t2,t3,t4};
foreach(var tt in taskArray)
tt.Start();
var finalTask = TaskExtension<string>.FirstSuccess(taskArray,"good");
Console.WriteLine(finalTask.Result);
Вы можете даже вернуть Task<Task<T>>
вместо Task<T>
для необходимой логической обработки