Parallel replaces thread-creation and task-collection boilerplate with static methods for concurrent delegates and loop iterations. Invoke runs delegates; For and ForEach parallelize synchronous loops; ForEachAsync accepts an asynchronous body and returns a Task that completes after all iterations finish. section 8.1.4
ForEach is a small change from ordinary iteration, but its workload still needs sensible limits. Parallel supports cancellation, custom schedulers, and MaxDegreeOfParallelism; choosing that maximum is a central performance concern, especially for large collections. section 8.1.4 section 8.3
Mail merge
A mail-merge operation can process recipients with Parallel.ForEach, constructing each message and sending it independently. The example catches failures per recipient and logs the failed address rather than allowing one send to define the whole operation.
Listing 8.9 Parallel.ForEach performance benchmark
for (int j = 0; j < 5; ++j)
{
var items = Enumerable.Range(0, 1000).ToArray();
var sw = Stopwatch.StartNew();
Parallel.ForEach(items,
(item)=>Thread.Sleep(1000));
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
Benchmark Parallel.ForEach over 1,000 one-second delays to observe completion time and evaluate an appropriate concurrency level. section 8.1.4
Listing 8.10 Asyncronous mail merge with the Parallel class
void MailMerge(
string from,
string subject,
string text,
(string email,string name)[] recipients)
{
var processingTasks = new Task[recipients.Length];
Parallel.ForEachAsync(recipients, #1
new ParallelOptions {
MaxDegreeOfParallelism= recipients.Length #2
},
async (current,_) => #3
{
try
{
var sender = new SmtpClient("smtp.example.com");
var message = new MailMessage();
message.From = new MailAddress(from);
message.Subject = subject;
message.To.Add(new MailAddress(current.email));
message.Body = text.Replace("{name}", current.name);
await sender.SendMailAsync(message); #4
}
catch
{
LogFailure(current.email);
}
}).Wait(); #5
}
Use Parallel.ForEachAsync when each iteration awaits asynchronous I/O, and configure MaxDegreeOfParallelism deliberately instead of assuming unlimited concurrency is appropriate. section 8.1.4
