How to control the relationship between multiple threads, without conflict and repetition, requires the use of mutually exclusive objects, namely: The Mutex class in the System.Threading namespace.
We can think of a mutex as a taxi and a passenger as a thread. Passengers first wait for the bus, then get on the bus and finally get off. When a passenger is in the car, the other passengers can only get on when he gets off the bus. This is also true of the thread's relationship to the mutex object, where the thread uses the Mutex.waitone () method to wait for the mutex object to be freed, and if it waits for the mutex object to be freed, it automatically owns the object until it calls Mutex.releasemutex () method to dispose of this object, while other threads that want to acquire this mutex object are only waiting.
The following example uses a mutex object to synchronize four threads, while the main thread waits for the end of four threads, and the four threads are run with two mutex objects.
It also uses the object of the AutoResetEvent class, which can be interpreted as a semaphore. Here the signal state is used to indicate the end of a thread.
AutoResetEvent.Set () method to set it to signaled state
The Autoresetevent.reset () method sets it to a no signal state
Example of a program for a Mutex class:
using System;
using System.Threading;
namespace Threadexample
{
public class Mutexsample
{
static Mutex gM1;
static Mutex gM2;
const int iters = 100;
static AutoResetEvent Event1 = new AutoResetEvent (false);
static AutoResetEvent Event2 = new AutoResetEvent (false);
static AutoResetEvent Event3 = new AutoResetEvent (false);
static AutoResetEvent Event4 = new AutoResetEvent (false);
public static void Main (string[] args)
{
Console.WriteLine ("Mutex Sample");
//Creates a mutex object and is named Mymutex
gM1 = new Mutex (True, "Mymutex");
//Creates an unnamed mutex object.
gM2 = new Mutex (true);
Console.WriteLine ("-Main owns GM1 and gM2");
autoresetevent[] EVs = new AUTORESETEVENT[4];
evs[0] = Event1; Define the AutoResetEvent object for the following thread T1,t2,t3,t4
evs[1] = Event2;
evs[2] = Event3;
evs[3] = Event4;
mutexsample TM = new Mutexsample ();
thread T1 = new Thread (new ThreadStart (Tm.t1start));
thread t2 = new Thread (new ThreadStart (Tm.t2start));
thread t3 = new Thread (new ThreadStart (Tm.t3start));
thread t4 = new Thread (new ThreadStart (Tm.t4start));
t1. Start ()///Use the Mutex.waitall () method to wait for an object in a mutex array to be released
T2. Start ();//Use the Mutex.waitone () method to wait for GM1 release
T3. Start ()///Use the Mutex.waitany () method to wait for any object in a mutex array to be freed
T4. Start ();//Use the Mutex.waitone () method to wait for GM2 release
Thread.Sleep (2000);
Console.WriteLine ("-Main releases gM1");
Gm1.releasemutex (); Thread T2,t3 end condition satisfies
Thread.Sleep (1000);
Console.WriteLine ("-Main releases gM2");
Gm2.releasemutex (); Thread T1,t4 end condition satisfies
//wait for all four threads to end
WaitHandle.WaitAll (EVS);
Console.WriteLine ("Mutex Sample");
Console.ReadLine ();
}
public void T1start ()
{
Console.WriteLine ("T1start started, Mutex.waitall (mutex[])");
mutex[] gMs = new mutex[2];
Gms[0] = gm1;//Create a mutex array as an argument to the Mutex.waitall () method
gms[1] = gM2;
Mutex.waitall (gMs);/Waiting for gM1 and gM2 to be released
Thread.Sleep (2000);
Console.WriteLine ("T1start Finished, Mutex.waitall (mutex[)) satisfied");
Event1.set (); The thread ends, setting the Event1 to a signaled state
}
public void T2start ()
{
Console.WriteLine ("T2start started, Gm1.waitone ()");
Gm1.waitone ()/Waiting for the release of GM1
Console.WriteLine ("T2start Finished, Gm1.waitone () satisfied");
Event2.set ();//end of thread, set Event2 to signaled status
}
public void T3start ()
{
Console.WriteLine ("T3start started, Mutex.waitany (mutex[)");
mutex[] gMs = new mutex[2];
Gms[0] = gm1;//Creates a mutex array as an argument to the Mutex.waitany () method
gms[1] = gM2;
Mutex.waitany (gMs);//wait for any one of the Mutex objects in the array to be freed
Console.WriteLine ("T3start Finished, Mutex.waitany (mutex[)");
Event3.set ();//end of thread, set Event3 to signaled status
}
public void T4start ()
{
Console.WriteLine ("T4start started, Gm2.waitone ()");
Gm2.waitone ()/Waiting for GM2 to be released
Console.WriteLine ("T4start Finished, Gm2.waitone ()");
Event4.set ();//end of thread, set Event4 to signaled status
}
}
}
The output of the program:
Mutex Sample
- Main Owns gM1 and gM2
t1Start started, Mutex.WaitAll(Mutex[])
t2Start started, gM1.WaitOne( )
t3Start started, Mutex.WaitAny(Mutex[])
t4Start started, gM2.WaitOne( )
- Main releases gM1
t2Start finished, gM1.WaitOne( ) satisfied
t3Start finished, Mutex.WaitAny(Mutex[])
- Main releases gM2
t1Start finished, Mutex.WaitAll(Mutex[]) satisfied
t4Start finished, gM2.WaitOne( )
Mutex Sample
It is clear from the results of the execution that the thread t2,t3 run is conditional on the release of GM1, and T4 begins execution after gM2 release, and T1 is executed after gM1 and GM2 are freed. At the end of the Main () function, you use WaitHandle to wait for all the AutoResetEvent objects ' signals, which represent the end of the corresponding thread.