Je comprends ce qu'est livelock, mais je me demandais si quelqu'un en avait un bon exemple basé sur le code? Et par code, je ne veux pas dire "deux personnes essayant de se croiser dans un couloir". Si je relis cela, je perdrai mon déjeuner.
concurrency
livelock
Alex Miller
la source
la source
Réponses:
Voici un exemple Java très simple de livelock où un mari et une femme essaient de manger de la soupe, mais n'ont qu'une cuillère entre eux. Chaque conjoint est trop poli et passera la cuillère si l'autre n'a pas encore mangé.
la source
getOwner
méthode ne doit-elle pas être synchronisée également? From Effective Java "la synchronisation n'a aucun effet sauf en lecture et en écriture ".Thread.join()
plutôt queThread.sleep()
, parce qu'il veut attendre que le conjoint fasse son repas?getOwner
méthode doit être synchronisée car même si lesetOwner
est synchronisé, cela ne garantit pas que le thread utilisantgetOwner
(ou accédantowner
directement au champ ) verra les modifications apportées par l'autre thread s'exécutersetOwner
. Cette vidéo explique cela très attentivement: youtube.com/watch?v=WTVooKLLVT8synchronized
mot-clé pour lasetOwner
méthode, car la lecture et l'écriture sont des actions atomiques pour la variable de référence.Mis à part les commentaires désinvoltes, un exemple connu est celui du code qui tente de détecter et de gérer les situations de blocage. Si deux threads détectent une impasse, et essaient de "s'écarter" l'un pour l'autre, sans se soucier ils finiront par se retrouver coincés dans une boucle toujours "écartés" et ne parvenant jamais à avancer.
Par «pas de côté», je veux dire qu'ils libéreraient le verrou et tenteraient de laisser l'autre l'acquérir. Nous pourrions imaginer la situation avec deux threads faisant cela (pseudocode):
Mis à part les conditions de course, ce que nous avons ici est une situation où les deux threads, s'ils entrent en même temps, finiront par courir dans la boucle intérieure sans continuer. C'est évidemment un exemple simplifié. Une solution naïve serait de mettre une sorte d'aléatoire dans le temps d'attente des threads.
La solution appropriée est de toujours respecter l' héritier des verrous . Choisissez une commande dans laquelle vous acquérez les serrures et respectez-la. Par exemple, si les deux threads acquièrent toujours lock1 avant lock2, alors il n'y a aucune possibilité de blocage.
la source
Comme il n'y a pas de réponse marquée comme réponse acceptée, j'ai tenté de créer un exemple de verrouillage en direct;
Le programme original a été écrit par moi en avril 2012 pour apprendre différents concepts du multithreading. Cette fois, je l'ai modifié pour créer un blocage, une condition de course, un blocage de la vie, etc.
Alors, comprenons d'abord l'énoncé du problème;
Problème de cookie Maker
Il existe quelques conteneurs d'ingrédients: ChocoPowederContainer , WheatPowderContainer . CookieMaker prend une certaine quantité de poudre des contenants d'ingrédients pour cuire un cookie . Si un fabricant de cookies trouve un conteneur vide, il recherche un autre conteneur pour gagner du temps. Et attend que Filler remplisse le conteneur requis. Il y a un remplisseur qui vérifie le conteneur à intervalles réguliers et remplit une certaine quantité si un conteneur en a besoin.
Veuillez vérifier le code complet sur github ;
Laissez-moi vous expliquer la mise en œuvre en bref.
Jetons un œil dans le code:
CookieMaker.java
IngredientContainer.java
Tout fonctionne bien jusqu'à ce que Filler remplisse les conteneurs. Mais si j'oublie de démarrer le remplisseur, ou si le remplisseur part en congé inopiné, les sous-threads continuent de changer d'état pour permettre à d'autres fabricants d'aller vérifier le conteneur.
J'ai également créé un démon ThreadTracer qui surveille les états des threads et les blocages. C'est la sortie de la console;
Vous remarquerez que les sous-threads et changer leurs états et attendre.
la source
Un exemple réel (bien que sans code exact) est le verrouillage direct de deux processus concurrents dans le but de corriger un blocage du serveur SQL, chaque processus utilisant le même algorithme d'attente-nouvelle tentative pour réessayer. Bien que ce soit la chance du timing, j'ai vu cela se produire sur des machines séparées avec des caractéristiques de performances similaires en réponse à un message ajouté à un sujet EMS (par exemple, enregistrer une mise à jour d'un seul graphique d'objet plus d'une fois), et ne pas pouvoir contrôler l'ordre de verrouillage.
Une bonne solution dans ce cas serait d'avoir des consommateurs concurrents (éviter les doubles traitements aussi haut que possible dans la chaîne en partitionnant le travail sur des objets indépendants).
Une solution moins souhaitable (ok, dirty-hack) est de briser la malchance de timing (genre de différences de force dans le traitement) à l'avance ou de la briser après une impasse en utilisant différents algorithmes ou un élément aléatoire. Cela pourrait toujours avoir des problèmes car il est possible que l'ordre de prise de verrouillage soit "collant" pour chaque processus, et cela prend un certain temps minimum non pris en compte dans l'attente-nouvelle tentative.
Une autre solution (au moins pour SQL Server) consiste à essayer un niveau d'isolement différent (par exemple, un instantané).
la source
J'ai codé l'exemple de 2 personnes passant dans un couloir. Les deux fils s'éviteront dès qu'ils se rendront compte que leurs directions sont les mêmes.
la source
Version C # du code de jelbourn:
la source
Un exemple ici pourrait être l'utilisation d'un tryLock chronométré pour obtenir plus d'un verrou et si vous ne pouvez pas tous les obtenir, reculez et réessayez.
Je pourrais imaginer qu'un tel code serait problématique car de nombreux threads entrent en collision et attendent d'obtenir un ensemble de verrous. Mais je ne suis pas sûr que ce soit un exemple très convaincant pour moi.
la source
tryLockAll()
avec les verrouslocks
dans le même ordre, il n'y a pas de verrouillage en direct.Version Python du code de jelbourn:
la source
Je modifie la réponse de @jelbourn. Quand l'un d'eux remarque que l'autre a faim, il (elle) doit relâcher la cuillère et attendre un autre avertissement, pour qu'un vivelock se produise.
la source
la source