Awaiting a sequence of tasks to run sequentially

梦想与她 提交于 2021-02-05 11:28:26

问题


I would like to create a generic method to await for a sequence of tasks to finish sequentially, retrieving the result of each one. This is the code I've created:

public static class TaskMixin 
{
    public static async Task<IEnumerable<T>> AwaitAll<T>(this IEnumerable<Task<T>> tasks)
    {
        var results = new List<T>();
        foreach (var t in tasks)
        {
            results.Add(await t);
        }

        return results;
    }
}

Is there a better or built-in way to do it?

Notice

Before writing the above method I tried with the built-in Task.WhenAll method, but it caused me troubles because the tasks are using an Entity Framework's DbContext and they seem to run concurrently.

This is the exception I got when I used Task.WhenAll.

A second operation started on this context before a previous asynchronous operation completed


回答1:


There's no way of ensuring that the tasks passed to this method are not already running, in fact they probably are already started hot tasks. If the tasks are already running then your only awaiting them sequentially. If you want operations to run sequentially then you need to invoke the methods that return your tasks sequentially.

Or in the case of EF's DbContext it's typically best to use a new DbContext for each request. But if your doing many parallel queries then you might be approaching the problem the wrong way. Many parallel queries doesn't necessarily mean your queries are going to run faster.

You can however abstract away running operations sequentially by taking a Func<Task<T>> delegate like this:

public async Task<IEnumerable<T>> RunSequentiallyAsync<T>(Func<Task<T>>[] operations)
{
    var results = new List<T>();
    foreach (var op in operations)
    {
        results.Add(await op());
    }
    return results;
}



回答2:


another way of awaiting the tasks sequentially is using IAsyncEnumerable and the await foreach syntax.

here is an sample extension method and a console app to use it. the console app reverses the tasks to demonstrate that the tasks are indeed called sequentially.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SO60066033
{
    public class Program
    {
        static async Task Main(string[] args)
        {
            var someTasks = new Func<Task<int>>[]
            {
                () => Task.FromResult(1),
                () => Task.FromResult(2),
                () => Task.FromResult(3),
                () => Task.FromResult(4),
            };
            var results = someTasks.AwaitAll();
            await foreach (var result in results)
                Console.WriteLine(result);
            var reversedTasks = someTasks.Reverse().ToList();
            var reversedResults = reversedTasks.AwaitAll();
            await foreach (var result in reversedResults)
                Console.WriteLine(result);
        }
    }
    public static class TaskExtensions
    {
        public static async IAsyncEnumerable<T> AwaitAll<T>(this IEnumerable<Func<Task<T>>> tasks)
        {
            foreach (var task in tasks)
                yield return await task();
        }
    }
}


来源:https://stackoverflow.com/questions/60066033/awaiting-a-sequence-of-tasks-to-run-sequentially

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!