liveBook · Manning

Parallelism

As explained in C# Concurrency: Asynchronous and multithreaded programming

Parallel execution is the simultaneous execution of multiple operations by separate processing resources.

Parallelization processes independent work items simultaneously so an operation can finish sooner. A mail merge exposes the problem: sending messages sequentially can exceed a web request’s timeout, while concurrent sends wait mainly on the slowest message. section 8.0 section 8.1

Choose the execution mechanism according to workload and resource limits:

  • Use Thread for direct control, but bound concurrency because one thread per item can create thousands of threads and degrade server performance. section 8.1.1
  • Use the thread pool through ThreadPool.QueueUserWorkItem or Task.Run for efficient, self-tuning scheduling; expect slower ramp-up when submitting a large burst. section 8.3
  • Prefer asynchronous operations for I/O-bound work that mostly waits, because blocked worker threads otherwise overwhelm the thread pool. section 8.1.3
  • Use Parallel for simpler thread-pool syntax, but measure and configure MaxDegreeOfParallelism for large collections. section 8.3

Mail merge and thread overhead

A thread-per-message mail merge starts one thread for every recipient and waits for all threads. A benchmark creating 1,000 sleeping threads completes in roughly 1.1–1.2 seconds outside the debugger, while 100,000 threads take 15–20 seconds. An asynchronous version replaces Send with SendMailAsync and Task.Delay for waiting work, measuring about 1,001–1,017 milliseconds for 1,000 tasks. section 8.1.1 section 8.1.3

Listing 8.6 Asynchronous mail merge using Task.Run
void MailMerge(
   string from,
   string subject,
   string text, 
   (string email,string name)[] recipients)
{
   var processingTasks = new Task[recipients.Length];
   for(int i=0;i< recipients.Length;++i)
   {
      processingTasks[i] = Task.Run(async ()=>               #1
      {
         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(recipients[i].email));
            message.Body = text.Replace("{name}", recipients[i].name);
            await sender.SendMailAsync(message);             #2
         }
         catch
         {
            LogFailure(current.email);
         }
      });
   }
   Task.WaitAll(processingTasks);
}
#1 Added async
#2 Added await

Inspect the asynchronous mail-merge listing showing Task.Run, SendMailAsync, per-recipient tasks, and exception logging. section 8.1.3

How C# Concurrency: Asynchronous and multithreaded programming covers it

How deep it goes
Compares Thread, thread-pool, asynchronous, and Parallel implementations with benchmarks, but defers detailed queue design and delivery semantics.
Examples
Demonstrates mail merge, thread-per-message execution, sleeping-thread benchmarks, and asynchronous delay benchmarks.
What you can do after
Apply it via C# threads, thread-pool tasks, asynchronous APIs, or Parallel, then measure overhead and limit concurrency appropriately.
How it connects
Connects parallelization with threads, thread pools, async/await, Task.Run, the Parallel class, work queues, and persistent queues.

Also explained in

Compare how 38 books explain parallelism →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C# Concurrency: Asynchronous and multithreaded programming. The section links point to the parts of the book it draws on.