How to implement Task.WhenAny() with a predicate

前端 未结 5 1727
半阙折子戏
半阙折子戏 2020-12-06 21:03

I want to execute several asynchronous tasks concurrently. Each task will run an HTTP request that can either complete successfully or throw an exception. I need to aw

5条回答
  •  执笔经年
    2020-12-06 21:40

    public static Task> WhenFirst(IEnumerable> tasks, Func, bool> predicate)
    {
        if (tasks == null) throw new ArgumentNullException(nameof(tasks));
        if (predicate == null) throw new ArgumentNullException(nameof(predicate));
    
        var tasksArray = (tasks as IReadOnlyList>) ?? tasks.ToArray();
        if (tasksArray.Count == 0) throw new ArgumentException("Empty task list", nameof(tasks));
        if (tasksArray.Any(t => t == null)) throw new ArgumentException("Tasks contains a null reference", nameof(tasks));
    
        var tcs = new TaskCompletionSource>();
        var count = tasksArray.Count;
    
        Action> continuation = t =>
            {
                if (predicate(t))
                {
                    tcs.TrySetResult(t);
                }
                if (Interlocked.Decrement(ref count) == 0)
                {
                    tcs.TrySetResult(null);
                }
            };
    
        foreach (var task in tasksArray)
        {
            task.ContinueWith(continuation);
        }
    
        return tcs.Task;
    }
    

    Sample usage:

    var task = await WhenFirst(tasks, t => t.Status == TaskStatus.RanToCompletion);
    
    if (task != null)
        var value = await task;
    

    Note that this doesn't propagate exceptions of failed tasks (just as WhenAny doesn't).

    You can also create a version of this for the non-generic Task.

提交回复
热议问题