Summary of different implementation methods for C# multi-threading synchronization
AutoResetEvent
class MainClass { // the array of consumer threads private static List<Thread> consumers = new List<Thread> (); // the task queue private static Queue<Action> tasks = new Queue<Action>(); // the synchronisation object for locking the task queue private static readonly object queueLock = new object(); // this wait handle notifies consumers of a new task private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false); // the synchronisation object for locking the console color private static readonly object consoleLock = new object(); // enqueue a new task private static void EnqueueTask (Action task) { lock (queueLock) { (task); } (); } // thread work method for consumers private static void DoWork(ConsoleColor color) { while (true) { // get a new task Action task = null; lock (queueLock) { if ( > 0) { task = (); } } if (task != null) { // set console to this task's color lock (consoleLock) { = color; } // execute task task (); } else // queue is empty - wait for a new task (); } } public static void Main (string[] args) { // set up 3 consumers (new Thread ( () => { DoWork(); })); (new Thread ( () => { DoWork(); })); (new Thread ( () => { DoWork(); })); // start all consumers ( (t) => { (); }); while (true) { // add a new task Random r = new Random(); EnqueueTask ( () => { // the task is to write a random number to the console int number = (10); (number); }); // random sleep to simulate workload ( (1000)); } } }
This code implements a simple producer-consumer model with one producer (simulated in the Main method) and multiple consumers (in this case three threads, each representing one consumer). This model synchronizes work between producers and consumers through shared task queues.
Code function overview:
Task Queue:use
Queue<Action>
to store the task to be executed (here, the task is defined as parameterlessAction
entrusted).Producer:exist
Main
In the method, new tasks are continuously generated through infinite loops (printing a random number to the console) and added these tasks to the task queue. Each time a producer generates a task, he waits randomly for a period of time (simulate workloads).consumer: There are three consumer threads, each performing the same
DoWork
method, but pass in different console color parameters. The consumer thread takes the task from the task queue and executes it (i.e. calls the task delegate), and sets the console color to its own color before executing the task. If the task queue is empty, the consumer thread will wait (through()
) until the producer notifies them that new tasks are available.-
Synchronization mechanism:
- use
queueLock
Objects to synchronize access to task queues, ensuring that data races do not occur when adding or removing tasks. - use
newTaskAvailable
(AutoResetEvent
) to inform consumers that new tasks are available. When the producer puts the task into the queue, it sets this event, thus awakening a waiting consumer thread. - use
consoleLock
Objects to synchronize settings on console color, preventing multiple consumer threads from changing console color at the same time.
- use
Code execution process:
initialization:exist
Main
In the method, create and start three consumer threads, each executingDoWork
method, but pass in different console color parameters.Producer cycle:
Main
The method enters an infinite loop, constantly generating new tasks (printing random numbers to the console) and adding these tasks to the task queue. After each task is generated, the producer will wait for a random period of time.Consumer work: Each consumer thread enters an infinite loop and tries to remove the task from the task queue. If the queue is not empty, they will set the console color to their own, perform the task (print the random number), and then try to remove the task from the queue again. If the queue is empty, they will wait
newTaskAvailable
Events are set, which usually occurs after the producer adds a new task to the queue.Continue to run: This process will continue because both producers and consumers run in an infinite loop. In practical applications, you may need to add some kind of exit mechanism to stop the program gracefully.
ManualResetEvent
class MainClass { // the array of consumer threads private static List<Thread> consumers = new List<Thread> (); // the task queue private static Queue<Action> tasks = new Queue<Action>(); // the synchronisation object for locking the task queue private static readonly object queueLock = new object(); // this wait handle notifies consumers of a new task private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false); // this wait handle pauses consumers private static EventWaitHandle pauseConsumers = new ManualResetEvent (true); // the synchronisation object for locking the console color private static readonly object consoleLock = new object(); // enqueue a new task private static void EnqueueTask (Action task) { lock (queueLock) { (task); } (); } // thread work method for consumers private static void DoWork(ConsoleColor color) { while (true) { // check if producer asked us to pause (); // get a new task Action task = null; lock (queueLock) { if ( > 0) { task = (); } } if (task != null) { // set console to this task's color lock (consoleLock) { = color; } // execute task task (); } else // queue is empty - wait for a new task (); } } public static void Main (string[] args) { // set up 3 consumers (new Thread ( () => { DoWork(); })); (new Thread ( () => { DoWork(); })); (new Thread ( () => { DoWork(); })); // start all consumers ( (t) => { (); }); bool consumersPaused = false; while (true) { // add a new task Random r = new Random(); EnqueueTask ( () => { // the task is to write a random number to the console int number = (10); (number); }); // random sleep to simulate workload ( (1000)); // pressing any key pauses/unpauses the consumers if () { (); if (consumersPaused) { (); ("Consumers resumed"); } else { (); ("Consumers paused"); } consumersPaused = !consumersPaused; } } } }
This code implements a producer-consumer model with pause/recovery functions, which contains a producer (inMain
Method simulation) and three consumer threads. This model synchronizes work between producers and consumers through shared task queues and allows users to pause or resume consumer execution through keystrokes.
Code function overview:
Task Queue:use
Queue<Action>
to store tasks to be executed.-
Synchronization mechanism:
-
queueLock
: Used to synchronize access to task queues to ensure that data race does not occur when adding or removing tasks. -
newTaskAvailable
(AutoResetEvent
): Set this event to notify a waiting consumer thread when the producer adds a new task to the queue. -
pauseConsumers
(ManualResetEvent
): Allows the producer (or user) to pause and resume consumer execution. -
consoleLock
: Used to synchronize settings to console colors to prevent multiple consumer threads from changing console colors at the same time.
-
Consumer thread: Three consumer threads execute separately
DoWork
method, but pass in different console color parameters. The consumer thread waits for a new task in infinite loop. If there are tasks in the task queue, it will take out the task and execute it, and set the console color to its own color. If the task queue is empty, waitnewTaskAvailable
Events are set. In addition, consumers will checkpauseConsumers
Whether the event is set, and if set, execution is paused until reset.Producer:exist
Main
The method is simulated, and new tasks are constantly generated (printing a random number to the console) and added to the task queue. After each task is generated, the producer will wait randomly for a period of time (simulating the workload). In addition, the producer also checks whether there are key inputs on the console, and if so, switches the consumer's pause/resume state.
Code execution process:
initialization: Create and start three consumer threads, each executing
DoWork
method, but pass in different console color parameters.Producer cycle:
Main
The method enters an infinite loop, constantly generating new tasks (printing random numbers to the console) and adding these tasks to the task queue. After each task is generated, the producer will wait for a random period of time. At the same time, the producer checks whether there are key inputs on the console and switches the consumer's pause/restoration status according to the keys.Consumer work: Each consumer thread enters an infinite loop, first check
pauseConsumers
Whether the event is set, and if set, wait. Then try to get the task out of the task queue, if the queue is not empty, set the console color and execute the task; if the queue is empty, waitnewTaskAvailable
Events are set.Pause/resume: The user can switch the consumer's pause/restoration status by pressing any key. If the consumer is currently in a pause state, the keys will wake them up and continue to execute; if the consumer is currently in a pause state, the keys will cause them to suspend execution.
CountdownEvent
class MainClass { // the array of consumer threads private static List<Thread> consumers = new List<Thread> (); // the task queue private static Queue<Action> tasks = new Queue<Action>(); // the synchronisation object for locking the task queue private static readonly object queueLock = new object(); // this wait handle notifies consumers of a new task private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false); // the wait handle to quit consumers private static CountdownEvent quitConsumers = new CountdownEvent (3); // the flag to request that consumers quit private static bool quitRequested = false; // the synchronisation object for quitting consumers private static readonly object quitLock = new object (); // the synchronisation object for locking the console color private static readonly object consoleLock = new object(); // enqueue a new task private static void EnqueueTask (Action task) { lock (queueLock) { (task); } (); } // thread work method for consumers private static void DoWork(ConsoleColor color) { while (true) { // check if someone asked us to quit lock (quitLock) { if (quitRequested) { ("Consumer {0} is quitting", color); (); break; } } // get a new task Action task = null; lock (queueLock) { if ( > 0) { task = (); } } if (task != null) { // set console to this task's color lock (consoleLock) { = color; } // execute task task (); } else // queue is empty - wait for a new task (1000); } } public static void Main (string[] args) { // set up 3 consumers (new Thread ( () => { DoWork(); })); (new Thread ( () => { DoWork(); })); (new Thread ( () => { DoWork(); })); // start all consumers ( (t) => { (); }); int iterations = 0; while (true) { // add a new task Random r = new Random(); EnqueueTask ( () => { // the task is to write a random number to the console int number = (10); (number); }); // random sleep to simulate workload ( (1000)); // quit after 10 iterations if (iterations++ >= 10) { // request consumer quit lock (quitLock) { quitRequested = true; } // wait until all consumers have signalled (); ("All consumers have quit"); break; } } } }
This code implements a producer-consumer model with an elegant exit mechanism. It creates three consumer threads that take out the shared task queue and execute tasks. Compared to previous code, this code adds the following major features and improvements:
-
Elegant exit mechanism:
- Introduced
quitRequested
Logo andquitLock
Synchronous object, used to control the exit request of the consumer thread. - use
CountdownEvent
(quitConsumers
) to wait for all consumer threads to complete the cleanup work before exit and send out a signal. - When the producer decides to exit, it will
quitRequested
The flag is set totrue
, and pass()
Wait for all consumer threads to call()
Come confirm that they are ready to exit.
- Introduced
-
Exit logic of consumer thread:
- Each consumer thread checks at the beginning of the loop
quitRequested
Logo. If set totrue
, the consumer will print an exit message and call()
to notify the producer it is ready to exit and exit the loop. - Note that the consumer will release the console color lock before exiting (
consoleLock
), but in this particular example, since exit occurs at an idle time when there is no task executable, this step may actually be redundant, as the console color is not accessed again when exiting.
- Each consumer thread checks at the beginning of the loop
-
Polling for task queues:
- The consumer will call it when the task queue is empty
(1000)
, this is a waiting call with timeout. This means that if no new tasks arrive within 1000 milliseconds, the consumer will stop waiting and check the exit conditions again. This helps prevent consumer threads from hang permanently when the queue is empty.
- The consumer will call it when the task queue is empty
-
Producer's iteration limit:
- The producer runs in a loop, but only performs a limited number of iterations (10 times in this example). Each iteration adds a new task to the task queue and waits randomly for a period of time between iterations to simulate the workload.
- When the iteration limit is reached, the producer requests the consumer to exit and waits for all consumers to be ready to exit.
-
Other synchronization mechanisms:
- The code is still used
queueLock
to synchronize access to task queues to prevent data race. -
consoleLock
Used to synchronize access to console colors to ensure that there is no conflict when multiple consumer threads try to change console colors at the same time.
- The code is still used
-
Code execution process:
- The program initializes three consumer threads and starts them.
- The producer runs in a loop, adding a new task to the task queue for each iteration and waiting for a random period of time.
- When the iteration limit is reached, the producer requests the consumer to exit and waits for them to confirm.
- Once all consumers confirm the exit, the producer will print a message and exit the program.
Barrier
class MainClass { // wait handles to rendezvous threads public static Barrier barrier = new Barrier(3, b => ("All threads have reached the barrier.")); // thread work method public static void DoWork() { for (int i = 0; i < 5; i++) { ( + ": " + i + " "); // rendezvous with other threads (); } } public static void Main(string[] args) { // start three threads new Thread(DoWork).Start(); new Thread(DoWork).Start(); new Thread(DoWork).Start(); // Keep the main thread alive to prevent the program from exiting before the threads finish ("Press Enter to exit..."); (); } }
Code function overview:
-
Barrier Initialization:
- exist
MainClass
In , aBarrier
Examplebarrier
, its constructor takes two parameters: the number of threads participating in synchronization (3 here) and a delegate called each time all threads reach the barrier (print a message).
- exist
-
Thread working methods:
-
DoWork
The method is the method that three threads will execute. Each thread will enter a loop, 5 times. - In each loop iteration, the thread prints its current number of iterations and thread ID, and then calls
()
。 -
SignalAndWait
The method causes the current thread to wait at the barrier until all other participating threads are also called.SignalAndWait
. Once all threads reach the barrier, they continue to execute at the same time and, if provided, the delegate specified in the constructor is executed (printing a message).
-
-
Main thread:
- The main thread started three
DoWork
Thread and wait for the user to press Enter to continue execution. This is to prevent the main thread from exiting the program before the background thread completes.
- The main thread started three
Running results
- When all three threads arrive
()
When they stop executing at the same time and wait for each other. - Once all threads reach the barrier, they continue to execute at the same time and the console prints "All threads have reached the barrier."
- This process is repeated at each loop iteration until each thread completes 5 iterations.
- Finally, after the user presses the Enter key, the program exits.
This is the end of this article about the different implementation methods of C# multi-threaded synchronization. For more related C# multi-threaded synchronization content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!
Related Articles
C# Call API to control Windows shutdown example (can be restarted/logged out)
This article introduces two methods of C# to control Windows shutdown, restart and logout, which are divided into methods of calling Windows' shutdown and calling API shutdown.2014-01-01Simple way to deal with Json in C#
This article mainly introduces the relevant information about Json's simple processing method in C#. It is very good and has reference value. Friends who need it can refer to it.2016-09-09Detailed explanation of cache usage in Winform
This article mainly introduces the use of cache in Winform. Friends who need it can refer to it.2014-01-01Sharing of examples for implementing array summation using recursion
This article mainly introduces the example of using recursion to implement array summing. The idea is to give an integer array a with n elements, find the sum of all elements in a. If you need it, please refer to it.2014-03-03C# realizes the function of simulated ATM automatic cash machine
This article introduces C# to implement the function of simulated ATM ATMs, and the article introduces it in detail through sample code. It has certain reference value for everyone's study or work. Friends who need it can refer to it.2022-08-08Detailed explanation of dynamic query in C# expressions [Translation]
This article mainly introduces relevant materials about dynamic query in C# expressions. The article introduces the example code in detail, which has certain reference learning value for everyone's study or work. Friends who need it, please learn with the editor below.2021-01-01String optimization in C# and detailed explanation of IsInterned
This article mainly introduces relevant materials about string optimization and IsInterned in C#. The article introduces it through sample code, which has certain reference learning value for everyone's study or work. Friends who need it, let's take a look.2017-12-12Sample code for WPF implementation calling native camera
This article mainly introduces how to use WPF to call the native camera. The sample code in the article explains in detail, which is helpful to our study or work. If you need it, please refer to it.2022-08-08C# Use AngleSharp library to parse html documents
This article introduces the method of using AngleSharp library to parse html documents in C#. The article introduces the example code in detail. It has certain reference value for everyone's study or work. Friends who need it can refer to it.2022-06-06Implementation of calling cross-domain MVC services based on C# background and Cookie verification
This article introduces the implementation of calling cross-domain MVC services based on C# background and cookie verification. If you need it, please refer to it2013-04-04