Semaphore et SemaphoreSlim

La classe System.Threading.Semaphore représente un sémaphore local ou nommé (visible à l'échelle du système). Il s'agit d'un wrapper mince entourant l'objet sémaphore Win32. Les sémaphores Win32 sont des sémaphores de comptage qui peuvent être utilisés pour contrôler l'accès à un pool de ressources.

La classe SemaphoreSlim représente un sémaphore léger et rapide qui peut être utilisé pour l'attente dans un processus unique quand les temps d'attente sont censés être très courts. SemaphoreSlim repose autant que possible sur les primitives de synchronisation fournies par le Common Language Runtime (CLR). Toutefois, il fournit également des handles d'attente initialisés tardivement et basés sur le noyau qui permettent l'attente de plusieurs sémaphores. SemaphoreSlim prend également en charge l'utilisation de jetons d'annulation, mais il ne prend pas en charge les sémaphores nommés ni l'utilisation d'un handle d'attente pour la synchronisation.

Gestion d'une ressource limitée

Les threads entrent dans le sémaphore en appelant la méthode WaitOne, qui est héritée de la classe WaitHandle, dans le cas d'un objet System.Threading.Semaphore, ou la méthode SemaphoreSlim.Wait ou SemaphoreSlim.WaitAsync dans le cas d'un objet SemaphoreSlim. Quand l'appel est retourné, le nombre du sémaphore est décrémenté. Quand un thread demande à entrer et que le nombre est égal à zéro, le thread se bloque. Quand les threads libèrent le sémaphore en appelant la méthode Semaphore.Release ou SemaphoreSlim.Release, les threads bloqués sont autorisés à entrer. Il n'existe aucun ordre garanti (tel que premier entré, premier sorti (FIFO) ou dernier entré, premier sorti (LIFO)), pour les threads bloqués devant entrer dans le sémaphore.

Un thread peut entrer dans le sémaphore plusieurs fois en appelant la méthode System.Threading.Semaphore de l'objet WaitOne ou la méthode SemaphoreSlim de l'objet Wait à plusieurs reprises. Pour libérer le sémaphore, le thread peut appeler la surcharge de méthode Semaphore.Release() ou SemaphoreSlim.Release() le même nombre de fois, ou appeler la surcharge de méthode Semaphore.Release(Int32) ou SemaphoreSlim.Release(Int32) et spécifier le nombre d’entrées à libérer.

Sémaphores et identité de thread

Les deux types de sémaphores n'appliquent pas l'identité de thread sur les appels aux méthodes WaitOne, Wait, Release et SemaphoreSlim.Release. Par exemple, un scénario classique d'utilisation des sémaphores implique un thread producteur et un thread consommateur, avec un thread incrémentant le nombre du sémaphore et l'autre le décrémentant.

Il est de la responsabilité du programmeur de garantir qu’un thread ne libère pas le sémaphore trop souvent. Supposons, par exemple, qu'un sémaphore ait un nombre maximal égal à deux et que le thread A et le thread B entrent dans le sémaphore. Si une erreur de programmation dans le thread B entraîne un double appel Release, les deux appels aboutiront. Le nombre maximal du sémaphore sera alors atteint, et quand le thread A appellera Release, une exception SemaphoreFullException sera levée.

Sémaphores nommés

Le système d'exploitation Windows permet d'attribuer un nom aux sémaphores. Un sémaphore nommé est disponible à l'échelle du système. C'est-à-dire qu'une fois créé, le sémaphore nommé est visible par tous les threads de tous les processus. Par conséquent, un sémaphore nommé peut être utilisé pour synchroniser aussi bien des activités de processus que des threads.

Vous pouvez créer un objet Semaphore qui représente un sémaphore système nommé en utilisant l'un des constructeurs qui spécifient un nom.

Notes

Étant donné que les sémaphores nommés sont disponibles à l'échelle du système, il est possible que plusieurs objets Semaphore représentent le même sémaphore nommé. Chaque fois que vous appelez un constructeur ou la méthode Semaphore.OpenExisting, un nouvel objet Semaphore est créé. Le fait de spécifier un même nom plusieurs fois crée plusieurs objets qui représentent le même sémaphore nommé.

Soyez prudent lorsque vous utilisez des sémaphores nommés. Étant donné qu'ils sont disponibles à l'échelle du système, un autre processus portant le même nom peut entrer dans votre sémaphore sans que vous vous y attendiez. Du code malveillant exécuté sur le même ordinateur pourrait s'en servir pour une attaque par déni de service.

Utilisez la sécurité de contrôle d'accès pour protéger un objet Semaphore qui représente un sémaphore nommé, de préférence en utilisant un constructeur qui spécifie un objet System.Security.AccessControl.SemaphoreSecurity. Vous pouvez également appliquer la sécurité de contrôle d'accès à l'aide de la méthode Semaphore.SetAccessControl. Sachez toutefois que cela crée une vulnérabilité entre le moment où le sémaphore est créé et celui où il est protégé. La protection des sémaphores grâce à la sécurité de contrôle d'accès empêche les attaques malveillantes, mais ne résout pas le problème des conflits de noms involontaires.

Voir aussi