Is there a generic Task.WaitAll?

C#GenericsTask Parallel-LibraryCovariance

C# Problem Overview


I start a few parallel tasks, like this:

var tasks =
	Enumerable.Range(1, 500)
	.Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue))
	.ToArray();

and then join them with

Task.WaitAll(tasks);

On this last line I get a blue squiggly marker under tasks, with a warning message:

Co-variant array conversion from Task[] to Task[]
can cause run-time exception on write operation.

I understand why I get this message, but is there a way around that? (for example, like a generic version of Task.WaitAll()?)

C# Solutions


Solution 1 - C#

A generic method of Task.WaitAll would imply that all Tasks would have to return the same type which would be extremely limited usefulness. Writting something like that could be done manually (see Bas Brekelmans answer), but this wont allow ContinueWith or cancellation without alot of work.

A simple solution if you aren't using the array for anything else is

  .ToArray<Task>();

Solution 2 - C#

I'm pretty sure it's a safe operation even with the warning, but if you really wanted to get around it a better option than creating your own implementation would be just to convert your tasks parameter into the type it wants:

Task.WaitAll(tasks.Cast<Task>().ToArray())

That kills the blue squiggles for me, lets me keep my tasks variable generic and doesn't force me to create a whole lot of new scary code that's ultimately unnecessary.

Solution 3 - C#

A BETTER AND SIMPLER ANSWER

Actually there IS a similar generic overload:

Task all = Task.WhenAll(tasks)

This is different in this it returns a Task that will complete after all tasks completed. so you can use await on it, or Wait(), whatever you want.

Look at the signature:

> ### Overloads ### > --------- NON GENERIC OVERLOADS -------------- > > WhenAll(IEnumerable<Task>) Creates a task that will > complete when all of the Task objects in an enumerable collection have > completed. > > WhenAll(Task[]) Creates a task that will complete when all of the > Task objects in an array have completed. > > --------- GENERIC OVERLOADS -------------- > > WhenAll<TResult>(IEnumerable<Task<TResult>>) Creates a task that will > complete when all of the Task<TResult> objects in an enumerable > collection have completed. > > WhenAll<TResult>(Task<TResult>[]) Creates a task that will complete > when all of the Task<TResult> objects in an array have completed.

Solution 4 - C#

You can create an extension method to do this.

I do not know the exact implementation of WaitAll, but we can assume it waits for every item to complete:

static class TaskExtensions
{
    public static void WaitAll<T>(this Task<T>[] tasks)
    {
        foreach (var item in tasks)
        {
            item.Wait();
        }
    }
}

Then call, from your current code:

tasks.WaitAll();

Edit

The actual implementation is a bit more complex. I have omitted the code from this answer because it is fairly long.

http://pastebin.com/u30PmrdS

You can modify this to support generic tasks.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCristian LupascuView Question on Stackoverflow
Solution 1 - C#MerickOWAView Answer on Stackoverflow
Solution 2 - C#DMac the DestroyerView Answer on Stackoverflow
Solution 3 - C#YitzchakView Answer on Stackoverflow
Solution 4 - C#BasView Answer on Stackoverflow