我尝试使用Task.WaitAny
等待一堆任务,但我真正想要的是等待第一个RanToCompletion
任务而不是Canceled
个任务。
所以当我有一堆状态如下的任务时:
0 Canceled;1 Canceled;2 Canceled;3 Canceled;4 Canceled;5 RanToCompletion;
理想情况下,我希望Task.WaitAny
返回5
,但返回的是0
。
我应该如何等待第一个RanToCompletion任务?
答案 0 :(得分:3)
没有开箱即用的东西。我们需要编写一些帮助方法,如评论中所述。
以下是使用TaskCompletionSource
的实现。
public class MyTask
{
private readonly TaskCompletionSource<Task> completionSource = new TaskCompletionSource<Task>();
private readonly Task[] tasks;
private int numberOfTasks;
private MyTask(Task[] tasks)
{
if (tasks.Length == 0)
{
throw new ArgumentException("No tasks");
}
this.tasks = tasks;
this.numberOfTasks= tasks.Length;
}
private int WaitAnyInternal()
{
foreach (var task in tasks)
{
task.ContinueWith(task1 => completionSource.TrySetResult(task1), TaskContinuationOptions.OnlyOnRanToCompletion);
}
foreach (var task in tasks)
{
task.ContinueWith(task1 =>
{
if (Interlocked.Decrement(ref numberOfTasks) == 0)
{
completionSource.SetCanceled();
}
}, TaskContinuationOptions.NotOnRanToCompletion);
}
try
{
completionSource.Task.Wait();
}
catch (AggregateException ex)
{
if (ex.Flatten().InnerExceptions.OfType<OperationCanceledException>().Any())
{
return -1;
}
}
return Array.IndexOf(tasks, completionSource.Task.Result);
}
public static int WaitAnyRanToCompletion(params Task[] tasks)
{
return new MyTask(tasks).WaitAnyInternal();
}
}
然后将其用作:
var task1 = Task.Run(() =>
{
Thread.Sleep(1000);
throw new Exception();
});//Faulted task
var task2 = Task.Run(() =>
{
Thread.Sleep(5000);
});//Will complete first
var task3 = Task.Delay(10000);//Will complete, but not first
int index = MyTask.WaitAnyRanToCompletion(task1, task2, task3);
//Index will be 1, which means task2