Menu

C# Synchronisation Cheat Sheet

20th November 2022 - c#, Development, dotnetcore, Uncategorised

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);
};