Un sémaphore est un concept de programmation fréquemment utilisé pour résoudre les problèmes de multithreading. Ma question à la communauté :
Qu'est-ce qu'un sémaphore et comment l'utiliser ?
Un sémaphore est un concept de programmation fréquemment utilisé pour résoudre les problèmes de multithreading. Ma question à la communauté :
Qu'est-ce qu'un sémaphore et comment l'utiliser ?
Imaginez les sémaphores comme les videurs d'une boîte de nuit. Un certain nombre de personnes sont autorisées à entrer dans la boîte en même temps. Si la boîte est pleine, personne n'est autorisé à entrer, mais dès qu'une personne part, une autre peut entrer.
Il s'agit simplement d'un moyen de limiter le nombre de consommateurs pour une ressource spécifique. Par exemple, pour limiter le nombre d'appels simultanés à une base de données dans une application.
Voici un exemple très pédagogique en C# :-)
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace TheNightclub
{
public class Program
{
public static Semaphore Bouncer { get; set; }
public static void Main(string[] args)
{
// Create the semaphore with 3 slots, where 3 are available.
Bouncer = new Semaphore(3, 3);
// Open the nightclub.
OpenNightclub();
}
public static void OpenNightclub()
{
for (int i = 1; i <= 50; i++)
{
// Let each guest enter on an own thread.
Thread thread = new Thread(new ParameterizedThreadStart(Guest));
thread.Start(i);
}
}
public static void Guest(object args)
{
// Wait to enter the nightclub (a semaphore to be released).
Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
Bouncer.WaitOne();
// Do some dancing.
Console.WriteLine("Guest {0} is doing some dancing.", args);
Thread.Sleep(500);
// Let one guest out (release one semaphore).
Console.WriteLine("Guest {0} is leaving the nightclub.", args);
Bouncer.Release(1);
}
}
}
Si c'est comme les videurs d'une boîte de nuit, il devrait laisser les invités entrer en séquence, mais quand je l'ai essayé, c'est aléatoire. Par exemple, l'invité 40 est entré en premier avant l'invité 39. Y a-t-il quelque chose que nous puissions faire pour contrôler cela ?
@TNA : Oui, cela a à voir avec la façon dont les nouveaux fils de discussion sont lancés dans cet exemple, et n'entre pas vraiment dans le cadre de la réponse.
L'analogie avec le videur est en effet épique, mais il est intéressant de noter qu'elle a déjà été utilisée : albahari.com/threading/part2.aspx#_Semaphore
L'article Mutex et sémaphores démystifiés de Michael Barr est une excellente introduction à ce qui différencie les mutex et les sémaphores, et quand les utiliser ou non. J'ai extrait plusieurs paragraphes clés ici.
Le point essentiel est que les mutex doivent être utilisés pour protéger les ressources partagées, tandis que les sémaphores doivent être utilisés pour la signalisation. Vous ne devriez généralement pas utiliser les sémaphores pour protéger les ressources partagées, ni les mutex pour la signalisation. Il y a des problèmes, par exemple, avec l'analogie du videur en termes d'utilisation des sémaphores pour protéger les ressources partagées - vous pouvez les utiliser de cette façon, mais cela peut causer des bogues difficiles à diagnostiquer.
Bien que les mutex et les sémaphores présentent certaines similitudes dans leur mise en œuvre, ils doivent toujours être utilisés différemment.
La réponse la plus courante (mais néanmoins incorrecte) à la question posée au début est que les mutex et les sémaphores sont très similaires, la seule différence significative étant que les sémaphores peuvent compter plus que un. Presque tous les ingénieurs semblent comprendre correctement qu'un mutex est un drapeau binaire utilisé pour protéger une ressource partagée en assurant l'exclusion mutuelle dans les sections critiques du code. Mais lorsqu'on leur demande d'expliquer comment utiliser un "sémaphore de comptage", la plupart des ingénieurs - à l'exception de leur degré de confiance - expriment une sorte d'opinion d'école selon laquelle ils sont utilisés pour protéger plusieurs ressources équivalentes.
...
A ce stade, une analogie intéressante est faite en utilisant l'idée de clés de salle de bain pour protéger les ressources partagées - la salle de bain. Si un magasin ne possède qu'une seule salle de bain, une seule clé suffira à protéger cette ressource et à empêcher plusieurs personnes de l'utiliser simultanément.
S'il y a plusieurs salles de bain, on pourrait être tenté de les coder de la même manière et de créer plusieurs clés - c'est un peu comme si un sémaphore était mal utilisé. Une fois que vous avez une clé, vous ne savez pas réellement quelle salle de bain est disponible, et si vous suivez cette voie, vous finirez probablement par utiliser des mutex pour fournir cette information et vous assurer que vous ne prenez pas une salle de bain qui est déjà occupée.
Un sémaphore n'est pas l'outil adéquat pour protéger plusieurs ressources essentiellement identiques, mais c'est ainsi que beaucoup de gens le conçoivent et l'utilisent. L'analogie du videur est nettement différente - il n'y a pas plusieurs ressources du même type, mais une seule ressource qui peut accepter plusieurs utilisateurs simultanés. Je suppose qu'un sémaphore peut être utilisé dans de telles situations, mais il est rare qu'il existe des situations réelles où l'analogie se vérifie - il est plus fréquent qu'il y ait plusieurs ressources du même type, mais toujours individuelles, comme les salles de bain, qui ne peuvent pas être utilisées de cette façon.
...
L'utilisation correcte d'un sémaphore est la signalisation d'une tâche à une autre. Un mutex est destiné à être pris et libéré, toujours dans cet ordre, par chaque tâche qui utilise la ressource partagée qu'il protège. En revanche, les tâches qui utilisent des sémaphores signalent ou attendent, mais pas les deux. Par exemple, la tâche 1 peut contenir du code pour afficher (c'est-à-dire signaler ou incrémenter) un sémaphore particulier lorsque le bouton "power" est pressé et la tâche 2, qui réveille l'écran, dépend de ce même sémaphore. Dans ce scénario, une tâche est le producteur du signal d'événement, l'autre le consommateur.
...
Il est important de souligner que les mutex interfèrent de manière négative avec les systèmes d'exploitation en temps réel, en provoquant une inversion de priorité où une tâche moins importante peut être exécutée avant une tâche plus importante en raison du partage des ressources. En bref, cela se produit lorsqu'une tâche de moindre priorité utilise un mutex pour s'emparer d'une ressource, A, puis tente de s'emparer de B, mais est mise en pause parce que B est indisponible. Pendant qu'elle attend, une tâche plus prioritaire arrive et a besoin de A, mais celui-ci est déjà occupé, et par un processus qui n'est même pas en cours d'exécution parce qu'il attend B. Il existe de nombreuses façons de résoudre ce problème, mais il est le plus souvent résolu en modifiant le mutex et le gestionnaire de tâches. Le mutex est beaucoup plus complexe dans ces cas qu'un sémaphore binaire, et l'utilisation d'un sémaphore dans un tel cas provoquera des inversions de priorité car le gestionnaire de tâches n'est pas conscient de l'inversion de priorité et ne peut pas agir pour la corriger.
...
La cause de la confusion moderne répandue entre les mutex et les sémaphores est historique, puisqu'elle remonte à l'invention en 1974 du sémaphore (S majuscule dans cet article) par Djikstra. Avant cette date, aucun des mécanismes de synchronisation et de signalisation des tâches à l'abri des interruptions connus des informaticiens n'était efficacement évolutif pour être utilisé par plus de deux tâches. Le sémaphore révolutionnaire, sûr et évolutif de Dijkstra a été appliqué à la fois à la protection des sections critiques et à la signalisation. Et c'est ainsi que la confusion a commencé.
Cependant, il est devenu évident pour les développeurs de systèmes d'exploitation, après l'apparition des RTOS préemptifs basés sur les priorités (par exemple, VRTX, vers 1980), la publication d'articles académiques établissant la RMA et les problèmes causés par l'inversion de priorité, et un article sur les protocoles d'héritage de priorité en 1990, que les mutex devaient être plus que de simples sémaphores avec un compteur binaire.
Mutex : partage des ressources
Sémaphore : signalisation
N'utilisez pas l'un pour l'autre sans tenir compte des effets secondaires.
Regardez ce document PDF sur la concurrence de Stanford. Regardez les pages 8. L'explication ci-dessus aura plus de sens alors see.stanford.edu/materials/icsppcs107/
@KrisSubramanian Merci pour le lien. Mais, le document traite des sémaphores et rien sur les Mutex. Cependant, voulez-vous dire que le tampon partagé dans l'exemple pourrait être protégé en utilisant Mutex ? au lieu d'avoir 2 sémaphores emptyBuffers et fullBuffers.
Mutex : accès à une ressource en tant que membre exclusif.
Sémaphore : accès à une ressource par n membres.
C'est-à-dire qu'un mutex peut être utilisé pour synchroniser l'accès à un compteur, un fichier, une base de données, etc.
Un sempahore peut faire la même chose mais supporte un nombre fixe d'appelants simultanés. Par exemple, je peux envelopper mes appels à la base de données dans un sémaphore(3) de sorte que mon application multithread ne puisse établir que trois connexions simultanées au maximum avec la base de données. Toutes les tentatives seront bloquées jusqu'à ce que l'un des trois créneaux se libère. Ils rendent des choses comme l'étranglement naïf vraiment, vraiment facile.
Selon Richard W. Stevens, un mutex est en fait un sémaphore binaire, avec seulement deux valeurs possibles : 0 et 1.
@QiangXu en Internes des systèmes d'exploitation et principes de conception par William Stallings, un sémaphore binaire est différent d'un mutex d'une manière très importante, et je cite : "Une différence essentielle entre un mutex et un sémaphore binaire est que le processus qui verrouille le mutex doit être celui qui le déverrouille. En revanche, il est possible pour un processus de verrouiller un sémaphore binaire et pour un autre de le déverrouiller." .
Au risque de commenter un fil de discussion dépassé, ce n'est pas correct. Comme @AdamDavis l'a mentionné plus haut, le sémaphore ne devrait (doit ?) pas être utilisé pour l'accès de n membres à une ressource - cela devrait toujours être fait en utilisant un Mutex. Considérez l'analogie de la salle de bain dans le Coffeeshop avec plusieurs personnes attendant d'y accéder ou autrement plusieurs salles de bain avec des clés similaires pour les salles de bain. Le sémaphore doit plutôt être utilisé pour la signalisation entre les tâches.
@Craig :
Un sémaphore est une manière de verrouiller un ressource de sorte qu'il est garanti que pendant qu'un morceau de code est exécuté, seul ce morceau de code a accès à cette ressource. Cela empêche deux threads d'accéder simultanément à une ressource, ce qui pourrait causer des problèmes.
Ce n'est pas limité à un seul fil. Un sémaphore peut être configuré pour permettre à un nombre fixe de threads d'accéder à une ressource.
Le sémaphore peut également être utilisé comme un ... sémaphore. Par exemple, si vous avez plusieurs processus qui mettent des données en file d'attente, et une seule tâche qui consomme les données de la file d'attente. Si vous ne voulez pas que votre tâche de consommation interroge constamment la file d'attente pour connaître les données disponibles, vous pouvez utiliser un sémaphore.
Ici, le sémaphore n'est pas utilisé comme un mécanisme d'exclusion, mais comme un mécanisme de signalisation. La tâche consommatrice est en attente sur le sémaphore. La tâche productrice est en train de poster sur le sémaphore.
Ainsi, la tâche de consommation est exécutée quand et seulement quand il y a des données à mettre en file d'attente.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
24 votes
Un drapeau booléen dont la valeur est basée sur le fait qu'un compteur de nombres entiers a atteint sa limite supérieure désignée. Obfuscation au maximum !