Sort Tasks into order of completition

2020-02-14 05:29发布

问题:

I saw Jon Skeet give a talk a year or so ago where he showed a snippet of C# 5 that would take a list of Tasks and return them in the order they completed.

It made use of async/await and WhenAny and was quite beautiful but I can't for the life of me remember how it worked. Now I have need for it.

I'm hoping to figure out how to create a method with a signature similar to this..

Task<IEnumerable<T>> InOrderOfCompletion<T>(IEnumerable<T> tasks) where T : Task

And could be used as follows:

public async Task<int> DelayedInt(int i)
{
    await Task.Delay(i*100);
    return i;
}

[Test]
public async void Test()
{
    Task<int>[] tasks = new[] {5, 7, 1, 3, 2, 6, 4}.Select(DelayedInt).ToArray();
    IEnumerable<Task<int>> ordered = await InOrderOfCompletion(tasks);

    Assert.That(ordered.Select(t => t.Result).ToArray(), Is.EqualTo(new [] {1,2,3,4,5,6,7}));
}

I've come up with the following but it doesn't feel as simple as I remember

    async Task<IEnumerable<T>> InOrderOfCompletion<T>(IEnumerable<T> tasks) where T : Task
    {
        HashSet<Task> taskSet = new HashSet<Task>(tasks);
        List<T> results = new List<T>();
        while(taskSet.Count > 0)
        {
            T complete = (T) await Task.WhenAny(taskSet);
            taskSet.Remove(complete);
            results.Add(complete);
        }
        return results;
    }

Do anyone remember know the snippet I'm referring to or how this can be improved upon?

回答1:

Jon Skeet, Stephen Toub, and I all have slightly different approaches. Mine is available via NuGet if you don't want to write your own.

Actually, the key is to avoid Task.WhenAny because that will turn the algorithm from O(N) into O(N^2).