The common answer here is to use Parallel.ForEach
(well apart from John Wu's answer that you should really consider). While on-the-outset Parallel.ForEach
seems like an easy and good idea, its actually not the most optimal approach.
Here is the problem:
Parallel.ForEach
uses the thread pool. Moreover, IO bound operations will block those threads waiting for a device to respond and tie up resources.
- If you have CPU bound code, Parallelism is appropriate;
- Though if you have IO bound code, Asynchrony is appropriate.
In this case, sending mail is clearly I/O, so the ideal consuming code would be asynchronous.
Furthermore, to use asynchronous and parallel features of the .NET properly, you should also understand the concept of I/O threads.
Not everything in a program consumes CPU time. When a thread tries to read data from a file on disk or sends a TCP/IP packet through network, the only thing it does is delegate the actual work to a device; disk or network adapter; and wait for results.
It’s very expensive to spend a threads time on waiting. Even through threads sleep and don’t consume CPU time while waiting for the results, it doesn’t really pay off because it’s a waste of system resources.
To be simplistic, every thread holds memory for stack variables, local storage and so on. Also, the more threads you have, the more time it takes to switch among them.
Though, the nice thing about Parallel.ForEach
is its easy to implement, you can also set up options like Max Degree of Parallelism.
So what can you do...
You are best to use async
/await
pattern and/or some type of limit on concurrent tasks, another neat solution is to ActionBlock<TInput>
Class in the TPL dataflow library.
Dataflow example
var block = new ActionBlock<MySomething>(
mySomething => MyMethodAsync(mySomething),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 50 });
foreach (var something in ListOfSomethings)
{
block.Post(something );
}
block.Complete();
await block.Completion;
This approach gives you Asynchrony, it also gives you MaxDegreeOfParallelism
, it doesn't waste resources, and lets IO be IO without chewing up unnecessary resources
Disclaimer, DataFlow may not be where you want to be, however I just thought I'd give you some more information on the different
approaches on offer.