I miei due centesimi su Mutex e Semaphore in C #

La sincronizzazione dei thread viene utilizzata per impedire a più thread di accedere contemporaneamente a una risorsa condivisa. Mutex e Semaphore sono due dei concetti correlati più importanti. Capiamo cosa sono entrambi e quando dovremmo usarli.

Prima di iniziare la nostra discussione, diamo una rapida occhiata ai concetti di base. Un thread è la più piccola unità di esecuzione all'interno di un processo. In sostanza, il multi-threading ti aiuta a eseguire più attività contemporaneamente e quindi ad aumentare il throughput complessivo dell'applicazione.

Un Mutex è una primitiva di sincronizzazione che può funzionare tra i processi, ovvero può essere utilizzato per la sincronizzazione tra processi. Un semaforo al contrario è quello che ti permette di limitare il numero di thread che hanno accesso a una risorsa condivisa nello stesso momento. In sostanza, un semaforo è una forma più generalizzata di un mutex.

Un semaforo viene utilizzato per limitare il numero di thread che possono avere accesso a una risorsa condivisa contemporaneamente. In sostanza, viene utilizzato per limitare il numero di consumatori per una particolare risorsa condivisa contemporaneamente. È possibile sfruttare Semaphore per implementare il blocco non esclusivo e quindi limitare la concorrenza.

Notare che un Mutex viene utilizzato per il blocco esclusivo su una risorsa condivisa. In altre parole, un Mutex ti consente di acquisire un blocco che si esclude a vicenda: qualsiasi thread avrebbe accesso a una risorsa condivisa in un determinato momento. Il blocco esclusivo viene utilizzato per garantire che in un dato momento, un solo thread possa entrare in una sezione critica. Una sezione critica può essere definita come una struttura di dati o una risorsa condivisa da più thread, ma uno e solo un thread può accedervi in ​​un dato momento.

La classe System.Threading.Mutex rappresenta un Mutex e la classe System.Threading.Semaphore viene utilizzata per lavorare con i semafori. È possibile utilizzare il metodo WaitOne su un'istanza della classe Mutex per bloccare e utilizzare il metodo ReleaseMutex per sbloccare.

Mutex mutexObject = new Mutex(false, "Demo");

if (!mutexObject.WaitOne(TimeSpan.FromSeconds(10), false))

     {

             Console.WriteLine("Quitting for now as another instance is in execution...");

               return;

     }

Per creare un semaforo in C #, è necessario creare un'istanza della classe Semaphore. Quando si crea un'istanza di Semaphore, è necessario passare due argomenti al suo costruttore di argomenti. Mentre il primo argomento viene utilizzato per indicare il numero di voci di risorse iniziali, il secondo argomento viene utilizzato per specificare il numero massimo di voci di risorse simultanee. Si noti che se si desidera riservare tutti gli slot per i nuovi thread che verranno creati, è necessario specificare valori identici per entrambi questi parametri. Il frammento di codice seguente illustra come creare un semaforo in C #.

public static Semaphore threadPool = new Semaphore(3, 5);

Fare riferimento allo snippet di codice riportato sopra. L'istruzione precedente crea un oggetto semaforo denominato threadPool che può supportare un massimo di 5 richieste simultanee. Si noti che il conteggio iniziale è impostato su 3 come indicato nel primo parametro al costruttore. Ciò implica che 2 slot sono riservati per il thread corrente e 3 slot sono disponibili per altri thread. Scriviamo ora un po 'di codice!

Il frammento di codice seguente mostra come creare e avviare 10 thread usando la classe Thread disponibile nello spazio dei nomi System.Threading. Notare come è stato utilizzato il delegato ThreadStart.

for (int i = 0; i < 10; i++)

{

   Thread threadObject = new Thread(new ThreadStart(PerformSomeWork));

   threadObject.Name = "Thread Name: " + i;

   threadObject.Start();

}

Ecco il codice del metodo PerformSomeWork. Questo è il metodo che contiene effettivamente il codice per lavorare con i semafori.

private static void PerformSomeWork()

       {

           threadPool.WaitOne();

           Console.WriteLine("Thread {0} is inside the critical section...", Thread.CurrentThread.Name);

           Thread.Sleep(10000);

           threadPool.Release();

       }

Fare riferimento al metodo PerformSomeWork indicato sopra. Il metodo WaitOne viene chiamato sull'istanza Semaphore per bloccare il thread corrente finché non viene ricevuto un segnale. Il metodo Release viene chiamato nella stessa istanza per rilasciare il semaforo. Ecco l'elenco completo del codice come riferimento.

class SemaphoreDemo

   {

       public static Semaphore threadPool = new Semaphore(3, 5);

       public static void Main(string[] args)

       {

           for (int i = 0; i < 10; i++)

           {

               Thread threadObject = new Thread(new ThreadStart(PerformSomeWork));

               threadObject.Name = "Thread Name: " + i;

               threadObject.Start();

           }

           Console.ReadLine();

       }

       private static void PerformSomeWork()

       {

           threadPool.WaitOne();

           Console.WriteLine("Thread {0} is inside the critical section...", Thread.CurrentThread.Name);

           Thread.Sleep(10000);

           threadPool.Release();

       }

   }