liveBook · Manning

Multithreading

As explained in C# Concurrency: Asynchronous and multithreaded programming

The execution of multiple threads within a program, either concurrently through interleaving or simultaneously on multiple processor cores.

Multithreading runs multiple threads within one program. On a single core, the system interleaves work through context switching; on multiple cores, several threads can execute simultaneously. Threads make applications more responsive and can shorten CPU-bound processing, but they add scheduling overhead and coordination complexity. section 1.1 section 1.6

A thread is not permanently attached to a processor core. The operating system may move it between cores, while thread affinity can force a relationship that is rarely appropriate. More cores can reduce processing time, but they do not eliminate waiting time or multithreading overhead. section 1.2

Use multithreading when the work benefits from independent execution or background scheduling:

  • Process parts of a calculation in parallel to finish sooner. section 8.0
  • Move long-running work away from a native user-interface thread while returning results to that thread. section 6.2
  • Handle independent client requests or defer work for background processing; application frameworks often manage request concurrency already. section 8.0
  • Avoid assuming that more threads improve I/O workloads: threads blocked on file or network operations consume resources without advancing CPU work. section 5.1

Multithreading and asynchronous programming overlap but solve different waiting problems. Multithreading runs work in the background, commonly on thread-pool threads. Asynchronous I/O frees the waiting thread and resumes processing after the operating system signals completion. Combining both can perform CPU work after asynchronous operations without holding threads idle. section 1.4 section 5.0

Ten files: threads versus asynchronous I/O

A threaded implementation starts ten Task.Run operations and uses a local copy of the loop index so every delegate reads the intended file. This can use up to ten threads, including while disk reads wait. An asynchronous implementation starts File.ReadAllBytesAsync for each file and awaits Task.WhenAll, allowing the waiting thread to remain available. If each file also needs CPU processing, combine asynchronous reads with processing work rather than wrapping all I/O in unnecessary threads. section 5.1

Keeping a native UI responsive

Run LongCalculation with Task.Run, await it, and then call UpdateUIWithCalculationResults after await. The continuation returns to the UI thread in the described UI scenario, while the calculation runs on a background thread. section 6.2

Concurrent access creates failure modes absent from single-threaded execution. One thread can observe partially updated data, while unsynchronized access can produce race conditions. Deadlocks, starvation, synchronization errors, memory reordering, and incorrect shared-state assumptions require deliberate safeguards. Await cannot appear inside a lock block; rearrange the code so awaiting occurs outside the lock. section 7.0 section 5.5

Listing 5.3 Reading 10 files using multithreading
public void Read10Files()
{
  var tasks = new Task[10];
  for(int i=0;i<10;++i)
  {
     var icopy = i;
     tasks[i] = Task.Run(()=>File.ReadAllBytes($"{icopy}.txt"));
  }
  Task.WaitAll(tasks);
}

Inspect a complete C# implementation that reads ten files with Task.Run, including the captured-index safeguard. section 5.1

public async void button1_Click(object sender, EventArgs args)
{
   await Task.Run(()=>
   {
      LongCalculation();
   }); 
   UpdateUIWithCalculationResults();
}

Compare the asynchronous UI pattern that performs calculation work in the background and updates the interface after await. section 6.2

How C# Concurrency: Asynchronous and multithreaded programming covers it

How deep it goes
Explains interleaving, cores, context switching, thread pools, continuations, performance costs, and classic synchronization failures.
Examples
Demonstrates pizza and web-server workflows, multicore scheduling, ten-file processing, parallel calculations, and responsive WinForms updates.
What you can do after
Apply multithreading with Task.Run, compare it with asynchronous I/O, preserve UI responsiveness, and identify common concurrency hazards.
How it connects
Connects multithreading with asynchronous programming, Task, thread pools, await, locks, race conditions, deadlocks, and synchronization.

Also explained in

Compare how 11 books explain multithreading →

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.