This is a series of articles about Multi-threading,

A - Introduction

The first article in this series, Multi-Threading (1), Concept: What, Why, written on 01/30/2023, introduced the multi-threading concept. The following articles should be the implementations in different approaches.

The content of the article:

Note

Writing a summary article is somewhat quite difficult. Before one jumps into each topic, it is hard to have an overview; on the other hand, if writing a series of articles, when one makes them done, it seems not necessary to write a summary article.

This article on MultiThreading overview is actually my study notes recorded over decades. In the first half, I tried to make them logical, as written in this article, while in the second half of the article, I just simply list the topics there, written in another article, Multi-Threading (2-1), Different MultiThreading Topics. For myself, it is still a learning process; for others, one may see how others learn multi-threading.

B - Asynchronous programming patterns

What are Asynchronous programming patterns? We start from Microsoft definition, that is specifically indicating to .Net Environment:

The major features of these Asynchronous Programming Models:

Asynchronous Programming Model Pattern

APM Pattern (ref)

Event-Based Asynchronous Pattern

Event-Based Pattern (ref)

Task-based Asynchronous Pattern

Task-Based Pattern (ref)

C - Ways to implement Multithreading in C#

1 // Passing the method name to be executed in the ctor directly without using the ThreadStart delegate

         var thread1 = new Thread(DoSomeWork);
         thread1.Start();

2 // Passing the ThreadStart delegate, which points to a method to be executed

         var threadStart = new ThreadStart(DoSomeWork);
         var thread2 = new Thread(threadStart);
         thread2.Start();

3 // Passing the ParametrizedThreadStart delegate, which points to the method to be executed

         var parametrizedThreadStart = new ParameterizedThreadStart(DoSomeWorkWithParameter);
         var thread3 = new Thread(parametrizedThreadStart);
         thread3.Start(2);

4 // Passing a Lambda expression in the Thread class constructor and subsequently calling the Start method

         var thread4 = new Thread(() =>
         {
             int x = 5;
             for (int i = 0; i < x; i++)
             {
                 Console.WriteLine(i);
             }
         });

         thread4.Start();

5 // Leveraging ThreadPools, call ThreadPool.QueueUserWorkItem passing in the method name to be executed

         ThreadPool.QueueUserWorkItem(DoSomeWorkWithParameter);
         ThreadPool.QueueUserWorkItem(DoSomeWorkWithParameter, 4);

6 // Using TPL (Task Parallel Library). Create a Task<T>, where T is the return type of the method to be executed.

         Task<string> task = Task.Factory.StartNew<string>(DoSomeStringWork);
         var result = task.Result;

7 // Instantiating Task class directly

         var task = new Task(
                () =>
                {
                    Console.WriteLine("Worker thread!");
                    Thread.Sleep(1000);
                });

            task.Start();

8 // Using Asynchronous Delegates, also known as the APM pattern

Func<string, string> work = DoSomeStringWork;
IAsyncResult res = work.BeginInvoke("Hello", null, null);
string result1 = work.EndInvoke(res);

D - Using Thread class to do Multithreading and Asynchronous callback

ThreadStart is a system-defined delegate that represents the method that executes on the Thread.

System.Threading.ThreadStart worker = new ThreadStart(WorkerThreadMethod);

Two ways to instantiate a Thread:

Manage Thread lifetime: We use thread class properties to manage thread lifetime:

E - Manage Thread Safety and Synchronization:

To handle the atomicity and avoid the deadlock.

References: