The many forms of thread synchronisation with basic examples showing reset events, mutex and semaphore (semafore).
Object _lockObject=new();
ManualResetEvent _manualResetEvent=new(false);
AutoResetEvent _autoResetEvent=new(true);//set initial signal to allow first thread
Mutex _mutex=new();
Semaphore _semaphore1_1=new(1,1);
Semaphore _semaphore1_3=new(1,3);
var DoWork=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} starting");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} completed");
};
var DoWorkWithLock=()=>{
lock(_lockObject){
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} starting");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} completed");
}
};
var DoWorkWithMonitor=()=>{
try{
Monitor.Enter(_lockObject);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} starting");
Thread.Sleep(2000);
//throw exception will allow lock to be exited
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} completed");
}catch(Exception){
Console.WriteLine("error");
}finally{
Monitor.Exit(_lockObject);
}
};
var DoWorkWithManualReset=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} starting");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} completed");
};
var WriteMethodMRE=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing");
_manualResetEvent.Reset();
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing completed");
_manualResetEvent.Set();
};
var ReadMethodMRE=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting");
_manualResetEvent.WaitOne();//wait until resetEvent returns set
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Readiing completed");
};
var WriteMethodARE=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting");
_autoResetEvent.WaitOne();
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing completed");
_autoResetEvent.Set();//Only set from thread
};
var WriteMethodMutex=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting");
_mutex.WaitOne();
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing completed");
_mutex.ReleaseMutex();//Only set from thread
};
var WriteMethodSemaphore1_1=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting");
_semaphore1_1.WaitOne();
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing completed");
_semaphore1_1.Release();
};
var WriteMethodSemaphore1_3=()=>{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting");
_semaphore1_3.WaitOne();
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing completed");
_semaphore1_3.Release();
};
//Tests
var testAllThread=()=>{
Console.WriteLine("All Threads");
for(var i=0;i<5;i++){
new Thread(()=>DoWork()).Start();
}
};
var testThreadWithLock=()=>{
Console.WriteLine("All Threads with lock");
for(var i=0;i<5;i++){
new Thread(()=>DoWorkWithLock()).Start();
}
};
var testThreadWithMonitor=()=>{
Console.WriteLine("All Threads with monitor");
for(var i=0;i<5;i++){
new Thread(()=>DoWorkWithMonitor()).Start();
}
};
// https://learn.microsoft.com/en-us/dotnet/api/system.threading.manualresetevent?view=net-6.0
var testThreadWithManualResetEvent=()=>{
Console.WriteLine("All Threads with Manual Reset");
new Thread(()=>WriteMethodMRE()).Start();
for(var i=0;i<5;i++){
new Thread(()=>ReadMethodMRE()).Start();
}
};
// https://learn.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent?view=net-6.0
var testThreadWithAutoResetEvent=()=>{
Console.WriteLine("All Threads with Auto Reset");
for(var i=0;i<5;i++){
new Thread(()=>WriteMethodARE()).Start();
}
Thread.Sleep(3000);
};
//https://learn.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=net-6.0
var testThreadWithMutex=()=>{
//Mutex doesn't allow calling the Release from outside of synchroinisation.
Console.WriteLine("All Threads with Mutex");
for(var i=0;i<5;i++){
new Thread(()=>WriteMethodMutex()).Start();
}
Thread.Sleep(3000);
};
// https://learn.microsoft.com/en-us/dotnet/standard/threading/semaphore-and-semaphoreslim
// https://learn.microsoft.com/en-us/dotnet/api/system.threading.semaphore
var testThreadWithSemaphore1_1=()=>{
//Mutex doesn't allow calling the Release from outside of synchroinisation.
Console.WriteLine("All Threads with Semaphore 1,1");
for(var i=0;i<8;i++){
new Thread(()=>WriteMethodSemaphore1_1()).Start();
}
Thread.Sleep(3000);
};
var testThreadWithSemaphore1_3=()=>{
//Mutex doesn't allow calling the Release from outside of synchroinisation.
Console.WriteLine("All Threads with Semaphore 1,3");
for(var i=0;i<8;i++){
new Thread(()=>WriteMethodSemaphore1_3()).Start();
}
Thread.Sleep(3000);
};