Parallel.ForEach applies an operation to sequence elements potentially in parallel and returns only after all iterations finish. It preserves the shape of a foreach loop while moving iteration work into the Parallel class. section 8.1.4
Use the API with attention to concurrency controls:
- Pass a ParallelOptions value when cancellation or a maximum degree of parallelism is required. section 8.1.4
- Treat the maximum degree of parallelism as a major practical safeguard; unrestricted processing can create surprising performance characteristics. section 8.1.4
- Choose Parallel.ForEachAsync when each iteration is asynchronous; it returns a Task instead of blocking until completion. section 8.1.4
Listing 8.8 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.ForEach(recipients,
(current,_) =>
{
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);
sender.Send(message);
}
catch
{
LogFailure(current.email);
}
}).Wait();
}
A mail-merge implementation replaces a sequential foreach with Parallel.ForEach, performs SMTP work per recipient, and logs iteration failures. section 8.1.4
