Quelle est la différence entre un verrou mort et une condition de course autour en termes de programmation?
Pensez à une condition de concurrence en utilisant l'exemple traditionnel. Supposons que vous et un ami ayez une carte ATM pour le même compte bancaire. Supposons maintenant que le compte contienne 100 $. Considérez ce qui se passe lorsque vous essayez de retirer 10 $ et que votre ami essaie de retirer 50 $ exactement en même temps.
Pensez à ce qui doit arriver. Le guichet automatique doit prendre votre saisie, lire ce qui se trouve actuellement dans votre compte, puis modifier le montant. Notez qu'en termes de programmation, une instruction d'affectation est un processus en plusieurs étapes.
Alors, étiquetez à la fois vos transactions T1 (vous retirez 10 $) et T2 (votre ami retire 50 $). Maintenant, les chiffres ci-dessous, à gauche, représentent des pas de temps.
T1 T2
---------------- ------------------------
1. Read Acct ($100)
2. Read Acct ($100)
3. Write New Amt ($90)
4. Write New Amt ($50)
5. End
6. End
Une fois les deux transactions terminées, en utilisant cette chronologie, ce qui est possible si vous n'utilisez aucun mécanisme de verrouillage, le compte contient 50 $. C'est 10 $ de plus qu'il ne devrait (votre transaction est perdue pour toujours, mais vous avez toujours l'argent).
Il s'agit d'une condition de concurrence appelée. Ce que vous voulez, c'est que la transaction soit sérialisable , c'est-à-dire que vous entrelaciez les exécutions d'instructions individuelles, le résultat final sera exactement le même comme certains programmes série (ce qui signifie que vous les exécutez les uns après les autres sans entrelacement) des mêmes transactions. La solution, encore une fois, consiste à introduire le verrouillage; cependant, un verrouillage incorrect peut entraîner un verrouillage mort.
Un blocage se produit en cas de conflit d'une ressource partagée. C'est un peu comme un Catch-22.
T1 T2
------- --------
1. Lock(x)
2. Lock(y)
3. Write x=1
4. Write y=19
5. Lock(y)
6. Write y=x+1
7. Lock(x)
8. Write x=y+2
9. Unlock(x)
10. Unlock(x)
11. Unlock(y)
12. Unlock(y)
Vous pouvez voir qu'un blocage se produit au moment 7 car T2 essaie d'acquérir un verrou sur x
mais T1 détient déjà le verrou sur x
mais il attend un verrou sur y
, que détient T2.
C'est si mauvais. Vous pouvez transformer ce diagramme en un graphique de dépendance et vous verrez qu'il y a un cycle. Le problème ici est que x et y sont des ressources qui peuvent être modifiées ensemble.
Une façon d'éviter ce type de problème de blocage avec plusieurs objets de verrouillage (ressources) consiste à introduire un ordre. Vous voyez, dans l'exemple précédent, T1 verrouillé x
puis y
mais T2 verrouillé y
puis x
. Si les deux transactions respectent ici une règle de commande qui dit que "x
doit toujours être verrouillé avant y
", ce problème ne se produit pas. (Vous pouvez modifier l'exemple précédent en gardant cette règle à l'esprit et voir qu'aucun blocage ne se produit).
Ce sont des exemples triviaux et je viens d'utiliser les exemples que vous avez peut-être déjà vus si vous avez suivi un cours de premier cycle à ce sujet. En réalité, la résolution des problèmes de blocage peut être beaucoup plus difficile que cela, car vous avez généralement plus de deux ressources et quelques transactions en interaction.
J'espère que cela aide un peu. Comme toujours, utilisez Wikipedia comme point de départ pour les concepts CS:
Un blocage se produit lorsque deux threads (ou plus) se bloquent mutuellement. Habituellement, cela a quelque chose à voir avec les threads essayant d'acquérir des ressources partagées. Par exemple, si les threads T1 et T2 doivent acquérir les ressources A et B pour faire leur travail. Si T1 acquiert la ressource A, alors T2 acquiert la ressource B, T1 peut alors attendre la ressource B tandis que T2 attend la ressource A. Dans ce cas, les deux threads attendent indéfiniment la ressource détenue par l'autre thread. Ces threads seraient bloqués.
Les conditions de concurrence se produisent lorsque deux threads interagissent de manière négative (buggy) selon l'ordre exact d'exécution de leurs différentes instructions. Si un thread définit une variable globale, par exemple, puis un deuxième thread lit et modifie cette variable globale et le premier thread lit la variable, le premier thread peut rencontrer un bogue car la variable a changé de façon inattendue.
Impasse :
Race/Condition de course:
Dans le codage, nous devons éviter à la fois la race et l'impasse.
Je suppose que vous voulez dire "conditions de course" et non "conditions de course" (j'ai entendu ce terme ...)
Fondamentalement, un verrou mort est une condition dans laquelle le thread A attend la ressource X tout en maintenant un verrou sur la ressource Y et le thread B attend la ressource Y tout en maintenant un verrou sur la ressource X. Les threads bloquent en attendant que l'autre libère son serrures.
La solution à ce problème est (généralement) de vous assurer que vous prenez des verrous sur toutes les ressources dans le même ordre dans tous les threads. Par exemple, si vous verrouillez toujours la ressource X avant la ressource Y, mon exemple ne peut jamais aboutir à un blocage.
Une condition de concurrence critique est quelque chose où vous comptez sur une séquence particulière d'événements se produisant dans un certain ordre, mais cela peut être gâché si un autre thread s'exécute en même temps. Par exemple, pour insérer un nouveau nœud dans une liste liée, vous devez modifier l'en-tête de liste, généralement quelque chose comme ceci:
newNode->next = listHead;
listHead = newNode;
Mais si deux threads font cela en même temps, alors vous pourriez avoir une situation où ils s'exécutent comme suit:
Thread A Thread B
newNode1->next = listHead
newNode2->next = listHead
listHead = newNode2
listHead = newNode1
Si cela devait se produire, la modification de la liste par le thread B sera perdue car le thread A l'aurait écrasée. Cela peut être encore pire, selon la situation exacte, mais c'est l'essentiel.
La solution à ce problème consiste généralement à s'assurer que vous incluez les mécanismes de verrouillage appropriés (par exemple, retirez un verrou à chaque fois que vous souhaitez modifier la liste liée afin qu'un seul thread la modifie à la fois).