J'ai entendu ces mots liés à la programmation simultanée, mais quelle est la différence entre eux?
concurrency
locking
mutex
semaphore
Victor
la source
la source
Réponses:
Un verrou permet à un seul thread d'entrer dans la partie verrouillée et le verrou n'est partagé avec aucun autre processus.
Un mutex est identique à un verrou mais il peut être à l'échelle du système (partagé par plusieurs processus).
Un sémaphore fait la même chose qu'un mutex mais permet à x nombre de threads d'entrer, ceci peut être utilisé par exemple pour limiter le nombre de tâches gourmandes en CPU, en io ou en RAM s'exécutant en même temps.
Pour un article plus détaillé sur les différences entre mutex et sémaphore, lisez ici .
Vous avez également des verrous en lecture / écriture qui permettent à tout moment un nombre illimité de lecteurs ou 1 écrivain.
la source
Il y a beaucoup d'idées fausses concernant ces mots.
Ceci provient d'un article précédent ( https://stackoverflow.com/a/24582076/3163691 ) qui convient parfaitement ici:
1) Section critique = objet utilisateur utilisé pour permettre l'exécution d'un seul thread actif à partir de nombreux autres dans un même processus . Les autres threads non sélectionnés (@ acquisition de cet objet) sont mis en veille .
[Pas de capacité interprocessus, objet très primitif].
2) Mutex Semaphore (aka Mutex) = Objet noyau utilisé pour permettre l'exécution d'un seul thread actif parmi de nombreux autres, parmi différents processus . Les autres threads non sélectionnés (@ acquisition de cet objet) sont mis en veille . Cet objet prend en charge la propriété des threads, la notification de terminaison des threads, la récursivité (plusieurs appels «d'acquisition» à partir du même thread) et «l'évitement d'inversion de priorité».
[Capacité interprocessus, très sûre à utiliser, une sorte d'objet de synchronisation «de haut niveau»].
3) Counting Semaphore (aka Semaphore) = Objet noyau utilisé pour permettre l'exécution d' un groupe de threads actifs à partir de nombreux autres. Les autres threads non sélectionnés (@ acquisition de cet objet) sont mis en veille .
[La capacité interprocessus n'est cependant pas très sûre à utiliser car elle n'a pas les attributs «mutex» suivants: notification de terminaison de thread, récursivité?, «Évitement d'inversion de priorité»?, Etc.].
4) Et maintenant, en parlant de «spinlocks», d'abord quelques définitions:
Région critique = Région de mémoire partagée par 2 processus ou plus.
Lock = Une variable dont la valeur autorise ou refuse l'entrée d'une «région critique». (Il pourrait être implémenté comme un simple «drapeau booléen»).
Attente occupée = Test continu d'une variable jusqu'à ce qu'une certaine valeur apparaisse.
Finalement:
Spin-lock (aka Spinlock) = Un verrou qui utilise l' attente occupée . (L'acquisition du verrou se fait par xchg ou des opérations atomiques similaires ).
[Aucun thread en veille, principalement utilisé au niveau du noyau uniquement. Ineffcient pour le code de niveau utilisateur].
En dernier commentaire, je ne suis pas sûr mais je peux vous parier quelques gros dollars que les 3 premiers objets de synchronisation ci-dessus (# 1, # 2 et # 3) utilisent cette bête simple (# 4) dans le cadre de leur mise en œuvre.
Bonne journée!.
Références:
-Concepts en temps réel pour les systèmes embarqués par Qing Li avec Caroline Yao (CMP Books).
-Systèmes d'exploitation modernes (3e) par Andrew Tanenbaum (Pearson Education International).
-Programmation d'applications pour Microsoft Windows (4e) par Jeffrey Richter (Microsoft Programming Series).
En outre, vous pouvez jeter un œil à l' adresse suivante : https://stackoverflow.com/a/24586803/3163691
la source
La plupart des problèmes peuvent être résolus en utilisant (i) uniquement des verrous, (ii) uniquement des sémaphores, ..., ou (iii) une combinaison des deux! Comme vous l'avez peut-être découvert, ils sont très similaires: les deux empêchent les conditions de concurrence , les deux ont
acquire()
/release()
opérations, les deux provoquent le blocage / soupçon de zéro ou plusieurs threads ... Vraiment, la différence cruciale réside uniquement dans la façon dont ils se verrouillent et se déverrouillent .Pour les deux verrous / sémaphores, essayer d'appeler
acquire()
alors que la primitive est à l'état 0 entraîne la suspension du thread appelant . Pour les verrous - les tentatives pour acquérir le verrou dans l'état 1 ont réussi. Pour les sémaphores - les tentatives d'acquisition du verrou dans les états {1, 2, 3, ...} ont réussi.Pour les verrous à l'état 0, si le même thread qui avait précédemment appelé
acquire()
appelle maintenant la libération, la libération réussit. Si un thread différent a essayé cela - c'est à l'implémentation / bibliothèque de savoir ce qui se passe (généralement la tentative est ignorée ou une erreur est levée). Pour les sémaphores à l'état 0, n'importe quel thread peut appeler release et cela réussira (quel que soit le thread utilisé précédemment pour acquérir le sémaphore à l'état 0).De la discussion précédente, nous pouvons voir que les verrous ont une notion de propriétaire (le seul thread qui peut appeler release est le propriétaire), tandis que les sémaphores n'ont pas de propriétaire (n'importe quel thread peut appeler release sur un sémaphore).
Ce qui cause beaucoup de confusion, c'est que, dans la pratique, il existe de nombreuses variantes de cette définition de haut niveau.
Variations importantes à considérer :
acquire()
/release()
être appelé? - [Varie massivement ]Cela dépend de votre livre / professeur / langue / bibliothèque / environnement.
Voici un rapide tour d'horizon notant comment certaines langues répondent à ces détails.
C, C ++ ( pthreads )
pthread_mutex_t
. Par défaut, ils ne peuvent pas être partagés avec d'autres processus (PTHREAD_PROCESS_PRIVATE
), cependant les mutex ont un attribut appelé pshared . Lorsqu'il est défini, le mutex est partagé entre les processus (PTHREAD_PROCESS_SHARED
).sem_t
. Semblable aux mutex, les sémaphores peuvent être partagés entre des menaces de nombreux processus ou gardés privés pour les threads d'un seul processus. Cela dépend de l' argument pshared fourni àsem_init
.python ( threading.py )
threading.RLock
) est essentiellement le même que C / C ++pthread_mutex_t
. Les deux sont tous deux réentrants . Cela signifie qu'ils ne peuvent être déverrouillés que par le même fil qui l'a verrouillé. Il est vrai que lessem_t
sémaphores, lesthreading.Semaphore
sémaphores et lestheading.Lock
verrous ne sont pas réentrants - car c'est le cas où n'importe quel thread peut effectuer un déverrouillage du verrouillage / descente du sémaphore.threading.Semaphore
) est essentiellement le même quesem_t
. Bien qu'avecsem_t
, une file d'attente d'ID de threads est utilisée pour se souvenir de l'ordre dans lequel les threads ont été bloqués lors de la tentative de verrouillage pendant qu'il est verrouillé. Lorsqu'un thread déverrouille un sémaphore, le premier thread de la file d'attente (s'il y en a un) est choisi pour être le nouveau propriétaire. L'identificateur de thread est retiré de la file d'attente et le sémaphore est à nouveau verrouillé. Cependant, avecthreading.Semaphore
, un ensemble est utilisé à la place d'une file d'attente, donc l'ordre dans lequel les threads ont été bloqués n'est pas stocké - n'importe quel thread de l'ensemble peut être choisi pour être le prochain propriétaire.Java ( java.util.concurrent )
java.util.concurrent.ReentrantLock
) est essentiellement le même que celui de C / C ++pthread_mutex_t
et de Pythonthreading.RLock
en ce qu'il implémente également un verrou réentrant. Le partage de verrous entre les processus est plus difficile en Java car la JVM agit comme un intermédiaire. Si un thread essaie de déverrouiller un verrou qu'il ne possède pas, unIllegalMonitorStateException
est levé.java.util.concurrent.Semaphore
) est essentiellement le même quesem_t
etthreading.Semaphore
. Le constructeur des sémaphores Java accepte un paramètre booléen d' équité qui contrôle s'il faut utiliser un ensemble (faux) ou une file d'attente (vrai) pour stocker les threads en attente.En théorie, les sémaphores sont souvent discutés, mais en pratique, les sémaphores ne sont pas tellement utilisés. Un sémaphore ne contient que l'état d' un entier, si souvent il est plutôt rigide et plusieurs sont nécessaires à la fois, ce qui rend difficile la compréhension du code. De plus, le fait que n'importe quel thread puisse libérer un sémaphore est parfois indésirable. Des primitives / abstractions de synchronisation plus orientées objet / de niveau supérieur telles que des "variables de condition" et des "moniteurs" sont utilisées à la place.
la source
Jetez un oeil à Multithreading Tutorial par John Kopplin.
Dans la section Synchronisation entre les threads , il explique les différences entre les événements, les verrous, les mutex, les sémaphores et les temporisations.
la source
Je vais essayer de le couvrir d'exemples:
Verrouiller: Un exemple où vous utiliseriez
lock
serait un dictionnaire partagé dans lequel des éléments (qui doivent avoir des clés uniques) sont ajoutés.Le verrou garantirait qu'un thread n'entre pas dans le mécanisme de code qui vérifie que l'élément est dans le dictionnaire tandis qu'un autre thread (qui se trouve dans la section critique) a déjà passé cette vérification et ajoute l'élément. Si un autre thread essaie d'entrer un code verrouillé, il attendra (sera bloqué) jusqu'à ce que l'objet soit libéré.
Sémaphore: Disons que vous avez un pool de connexions, alors un seul thread peut réserver un élément dans le pool en attendant que le sémaphore obtienne une connexion. Il utilise ensuite la connexion et lorsque le travail est terminé, libère la connexion en libérant le sémaphore.
L'exemple de code que j'aime est celui d'un videur donné par @Patric - le voici:
Mutex Il est à peu près
Semaphore(1,1)
et souvent utilisé à l'échelle mondiale (à l'échelle de l'application sinon, illock
est sans doute plus approprié). On utiliserait globalMutex
lors de la suppression du nœud d'une liste accessible globalement (dernière chose que vous voulez qu'un autre thread fasse quelque chose pendant que vous supprimez le nœud). Lorsque vous acquérezMutex
si un thread différent essaie d'acquérir le même,Mutex
il sera mis en veille jusqu'à ce que le même thread qui l'a acquis leMutex
libère.Un bon exemple sur la création d'un mutex mondial est de @deepee
puis utilisez comme:
J'espère que cela vous fera gagner du temps.
la source
Wikipedia a une grande section sur les différences entre les sémaphores et les mutex :
la source
Ma compréhension est qu'un mutex est uniquement destiné à être utilisé dans un seul processus, mais sur ses nombreux threads, alors qu'un sémaphore peut être utilisé sur plusieurs processus et sur leurs ensembles de threads correspondants.
En outre, un mutex est binaire (il est verrouillé ou déverrouillé), tandis qu'un sémaphore a une notion de comptage ou une file d'attente de plusieurs demandes de verrouillage et déverrouillage.
Quelqu'un pourrait-il vérifier mon explication? Je parle dans le contexte de Linux, en particulier de Red Hat Enterprise Linux (RHEL) version 6, qui utilise le noyau 2.6.32.
la source
Utilisation de la programmation C sur une variante Linux comme cas de base pour des exemples.
Fermer à clé:
• Habituellement, un binaire de construction très simple en fonctionnement verrouillé ou déverrouillé
• Aucun concept de propriété de thread, de priorité, de séquencement, etc.
• Habituellement, un verrou tournant où le fil vérifie en permanence la disponibilité des verrous.
• Se fonde généralement sur des opérations atomiques, par exemple, tester et définir, comparer et échanger, extraire et ajouter, etc.
• Nécessite généralement un support matériel pour le fonctionnement atomique.
Verrous de fichiers:
• Habituellement utilisé pour coordonner l'accès à un fichier via plusieurs processus.
• Plusieurs processus peuvent contenir le verrou de lecture. Cependant, lorsqu'un seul processus détient le verrou d'écriture, aucun autre processus n'est autorisé à acquérir un verrou de lecture ou d'écriture.
• Exemple: flock, fcntl etc.
Mutex:
• Les appels de fonction Mutex fonctionnent généralement dans l'espace du noyau et entraînent des appels système.
• Il utilise le concept de propriété. Seul le thread qui détient actuellement le mutex peut le déverrouiller.
• Mutex n'est pas récursif (exception: PTHREAD_MUTEX_RECURSIVE).
• Habituellement utilisé en association avec les variables de condition et transmis comme arguments à pthread_cond_signal, pthread_cond_wait, etc.
• Certains systèmes UNIX permettent à mutex d'être utilisé par plusieurs processus bien que cela ne soit pas forcément appliqué sur tous les systèmes.
Sémaphore:
• Il s'agit d'un entier maintenu par le noyau dont les valeurs ne doivent pas tomber en dessous de zéro.
• Il peut être utilisé pour synchroniser les processus.
• La valeur du sémaphore peut être définie sur une valeur supérieure à 1, auquel cas la valeur indique généralement le nombre de ressources disponibles.
• Un sémaphore dont la valeur est limitée à 1 et 0 est appelé sémaphore binaire.
la source
Supporting ownership
,maximum number of processes share lock
Etmaximum number of allowed processes/threads in critical section
sont trois principaux facteurs qui déterminent le nom / type de l'objet en même temps que le nom général delock
. Étant donné que la valeur de ces facteurs est binaire (a deux états), nous pouvons les résumer dans un tableau de vérité 3 * 8.N'hésitez pas à éditer ou développer ce tableau, je l'ai posté en tant que tableau ascii pour être éditable :)
la source