How to implement an efficient WhenEach that streams an IAsyncEnumerable of task results?

后端 未结 4 944
闹比i
闹比i 2021-01-01 04:32

I am trying to update my toolset with the new tools offered by C# 8, and one method that seems particularly useful is a version of Task.WhenAll that returns an IAsyncEnumera

4条回答
  •  南方客
    南方客 (楼主)
    2021-01-01 04:57

    By using code from this article, you can implement the following:

    public static Task>[] Interleaved(IEnumerable> tasks)
    {
       var inputTasks = tasks.ToList();
    
       var buckets = new TaskCompletionSource>[inputTasks.Count];
       var results = new Task>[buckets.Length];
       for (int i = 0; i < buckets.Length; i++)
       {
           buckets[i] = new TaskCompletionSource>();
           results[i] = buckets[i].Task;
       }
    
       int nextTaskIndex = -1;
       Action> continuation = completed =>
       {
           var bucket = buckets[Interlocked.Increment(ref nextTaskIndex)];
           bucket.TrySetResult(completed);
       };
    
       foreach (var inputTask in inputTasks)
           inputTask.ContinueWith(continuation, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
    
       return results;
    }
    

    Then change your WhenEach to call the Interleaved code

    public static async IAsyncEnumerable WhenEach(Task[] tasks)
    {
        foreach (var bucket in Interleaved(tasks))
        {
            var t = await bucket;
            yield return await t;
        }
    }
    

    Then you can call your WhenEach as per usual

    await foreach (int result in WhenEach(tasks))
    {
        Console.WriteLine($"Processed: {result}");
    }
    

    I did some rudimentary benchmarking with 10k tasks and performed 5 times better in terms of speed.

提交回复
热议问题