如何在第一个任务RanToCompletion之后等待

时间:2014-11-06 07:42:48

标签: c# async-await

我尝试使用Task.WaitAny等待一堆任务,但我真正想要的是等待第一个RanToCompletion任务而不是Canceled个任务。

所以当我有一堆状态如下的任务时:

0 Canceled;1 Canceled;2 Canceled;3 Canceled;4 Canceled;5 RanToCompletion;

理想情况下,我希望Task.WaitAny返回5,但返回的是0

我应该如何等待第一个RanToCompletion任务?

1 个答案:

答案 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