I am using SignalR. The function on the Hub often return a Task. I now have a function that will add a connection to a bunch of groups. I would like to return a Task that represents all of these Tasks.
I found a perfect function for that: Task.WhenAll. However this is a new function in .NET 4.5 and I am still stuck on .NET 4.
Hence I decided to write my own version of it until we can move to .NET 4.5. Because there are often some caveats when it comes to multithreading (e.g. thread pool stuff), I am not sure if my implementation is correct:
public static Task WhenAll(IEnumerable<Task> tasks)
{
return Task.Factory.StartNew(() => Task.WaitAll(tasks.ToArray()));
}
Functionally, it works I think, but don't I get an extra blocked thread for the new Task? Or is this unavoidable?
Edit: Here is how I would use it with SignalR:
public static Task Add(this IGroupManager groupManager, string connectionId,
IEnumerable<string> groups)
{
return WhenAll(groups.Select(group => groupManager.Add(connectionId, group)));
}
Your solution will work fine, but you're right that it would block a thread the whole time.
I think the simplest way to efficiently implement WhenAll()
on .Net 4.0 is to use ContinueWhenAll()
. It performs an action when all Task
s from a collection are finished and returns a Task
representing that action. Since we want just that Task
, we don't need the action, passing an empty lambda will work:
public static Task WhenAll(IEnumerable<Task> tasks)
{
return Task.Factory.ContinueWhenAll(tasks.ToArray(), _ => {});
}
While you're targeting .Net 4.0, if you can use VS2012, then a simpler/better option (IMHO) is to use NuGet to install the async targeting pack and then you can use WhenAll (TaskEx.WhenAll in that case, since it can't modify the Task that's in the 4.0 framework).
As a significant added bonus, you can then use async/await in your .Net 4.0 code as well :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With