views:

76

answers:

4

Currently I have a section of code that needs to make about 7 web service calls to various providers for data. Each call takes a several seconds to execute, so I would like to run them in parallel to speed things up.

I have wrapped my 7 calls in a Parallel.Invoke which works great at running a couple things at the same time, but on a 2 core server, it will only execute 2 at a time, one per core. Since all I am doing is waiting around for the web service calls to return I would want it to go fetch all 7 and wait for them to come back.

Is there no way to do this? Or perhaps my approach is wrong? Maybe I need to create asynchronous calls to the web services? But then how to wait for them all to come back before moving on?

A: 

You can specify ParallelOptions to increase the concurrency level. The default is reasonable for CPU-bound tasks, but you're dealing with I/O-bound ones, so it makes sense to override that behavior.

Steven Sudit
I thought increasing the concurrency level only goes as far as the max concurrency allowed? ie if you have 8 cores, you can set the concurrency level to anything..but going higher than 8 won't do anything...
puffpio
@puffpio: All it says is: "The MaxDegreeOfParallelism limits the number of concurrent operations run by Parallel method calls that are passed this ParallelOptions instance to the set value, if it is positive. If MaxDegreeOfParallelism is -1, then there is no limit placed on the number of concurrently running operations."
Steven Sudit
Yeah that confuses me, since it says "limit" which to me means it will not go beyond the number of cores you have. I say that because the page on Parallel.Invoke says: "Note that with Invoke(), you simply express which actions you want to run concurrently, and the runtime handles all thread scheduling details, including scaling automatically to the number of cores on the host computer."
puffpio
+3  A: 

but on a 2 core server, it will only execute 2 at a time, one per core

I would question this - Parallel.Invoke will typically run many more tasks than cores in your system.

That being said, if you're using asynchronous method call invocations, I would recommend using Task.Factory.FromAsync(...) to generate your seven distinct tasks.

This provides the flexibility of doing other work while the tasks execute, then calling Task.WaitAll (or Task.WaitAny) when you decide to use the results.

Parallel.Invoke, on the other hand, will always block until all seven complete.

Reed Copsey
@Reed: I got the impression that he did want all of them to complete before continuing.
Steven Sudit
@Steven: Which he can do via Task.WaitAll - but he can do other work while he's waiting, if it's available, if that's an option. Cases like this, though, often work with Task.WaitAny, since you can usually start some of the work as soon as some of the tasks complete - it's rare that you need 7 different results to do any processing...
Reed Copsey
You're right, this will work, too, and more flexibly than what I suggested.
Steven Sudit
Thanks for the info. May I ask..is creating several Tasks then using Task.WaitAll just syntactic sugar for a Parallel.Invoke?
puffpio
@puffpio: Not quite - Parallel.Invoke doesn't allow returns (the tasks can actually treat the return values from the delegates as futures), and has some more flexibility in terms of when and how to wait. If the Task objects are not Task<T>, AND you just immediately do Task.WaitAll, it'll behave like Parallel.Invoke, though.
Reed Copsey
A: 

There's no real need to use Parallel.Invoke for this. You can go ahead and issue the multiple asynchronous requests (i.e. HttpWebRequest.BeginGetResponse()). As for being notified when they all complete, you have several options. A simple way would be to initialize a CountdownEvent before you issue the first request, and then have the main thread wait on that event after it's issued the requests. The asynchronous callback methods each signal that event as they complete. That way, you ensure that all requests have completed before your main thread continues.

Jim Mischel
But TPL is so much _easier_ than the APM. It would be nice if we could have the best of both worlds.
Henk Holterman
@Jim: Using Task.Factory.StartAsync provides the best of both worlds...without the need to track with CountdownEvent, etc.
Reed Copsey
I need to look more closely at the TPL.
Jim Mischel
A: 

I'm going to copy a response that I made to another question verbatim because it's equally, if not more, applicable here.


You simply cannot beat the Asynchronous Programming Model (APM) when it comes to I/O performance. Anytime you can use it, you should be. Luckily the Task Parallel Library (TPL) comes with baked in support for combining APM work into the mix with "pure" TPL tasks via the FromAsync factory method.

Check out this section of the .NET SDK on MSDN entitled TPL and Traditional .NET Asynchronous Programming for more information on how to combine these two programming models to achieve async nirvana.

Drew Marsh