Quelqu'un peut-il s'il vous plaît expliquer avec des exemples (de code) quelle est la différence entre impasse et livelock?
Tiré de http://en.wikipedia.org/wiki/Deadlock :
En calcul simultané, une impasse est un état dans lequel chaque membre d'un groupe d'actions attend qu'un autre membre lâche un verrou.
Un livelock est similaire à une impasse, sauf que les états des processus impliqués dans le livelock changent constamment les uns par rapport aux autres, aucun ne progressant. Livelock est un cas particulier de privation de ressources; la définition générale indique seulement qu'un processus spécifique ne progresse pas.
Un exemple concret de vie réelle se produit lorsque deux personnes se rencontrent dans un couloir étroit et que chacune essaie d'être polie en s'écartant pour laisser passer l'autre, mais elles finissent par se balancer d'un côté à l'autre sans faire aucun progrès de la même manière au même moment.
Livelock est un risque avec certains algorithmes qui détectent et sortent d'une impasse. Si plusieurs processus prennent des mesures, l'algorithme de détection d'interblocage peut être déclenché de manière répétée. Cela peut être évité en veillant à ce qu'un seul processus (choisi au hasard ou par priorité) prenne des mesures.
Un thread agit souvent en réponse à l'action d'un autre thread. Si l'action de l'autre thread est également une réponse à l'action d'un autre thread, alors livelock peut en résulter.
Comme dans le cas d'interblocage, les threads livelocked sont incapables de progresser . Cependant, les threads ne sont pas bloqués - ils sont simplement trop occupés à se répondre pour reprendre le travail . C'est comparable à deux personnes qui tentent de se croiser dans un couloir: Alphonse se déplace à sa gauche pour laisser passer Gaston, tandis que Gaston se déplace à sa droite pour laisser passer Alphonse. Voyant qu'ils se bloquent encore, Alphonse se place à sa droite, tandis que Gaston se déplace à sa gauche. Ils se bloquent encore, et ainsi de suite ...
La principale différence entre livelock et Le blocage indique que les threads ne seront pas bloqués. Ils essaieront plutôt de se répondre en permanence.
Dans cette image, les deux cercles (threads ou processus) essaieront de laisser de l'espace à l'autre en se déplaçant à gauche et à droite. Mais ils ne peuvent pas aller plus loin.
Tout le contenu et les exemples ici sont de
Systèmes d'exploitation: composants internes et principes de conception
William Stallings
Édition 8º
impasse: situation dans laquelle deux processus ou plus ne peuvent pas continuer car chacun attend qu'un autre fasse quelque chose.
Par exemple, considérons deux processus, P1 et P2, et deux ressources, R1 et R2. Supposons que chaque processus ait besoin d'accéder aux deux ressources pour effectuer une partie de sa fonction. Il est alors possible d’avoir la situation suivante: le système d’exploitation attribue R1 à P2 et R2 à P1. Chaque processus attend l'une des deux ressources. Aucune des deux ne libérera la ressource qu'elle possède déjà avant d'avoir acquis l'autre ressource et exécuté la fonction nécessitant les deux ressources. Les deux processus sont dans l'impasse
Livelock: situation dans laquelle deux processus ou plus changent continuellement d’état en réponse aux modifications apportées aux autres processus sans effectuer de travail utile:
Starvation: Une situation dans laquelle un processus exécutable est négligé indéfiniment par le planificateur; bien qu'il puisse continuer, il n'est jamais choisi.
Supposons que trois processus (P1, P2, P3) nécessitent chacun un accès périodique à la ressource R. Examinons la situation dans laquelle P1 est en possession de la ressource et que P2 et P3 sont tous deux en retard, en attente de cette ressource. Lorsque P1 quitte sa section critique, P2 ou P3 doivent être autorisés à accéder à R. Supposons que le système d'exploitation autorise l'accès à P3 et que P1 nécessite à nouveau un accès avant que P3 ne complète sa section critique. Si le système d'exploitation autorise l'accès à P1 à la fin de P3, puis alternativement à l'accès à P1 et à P3, P2 peut se voir refuser indéfiniment l'accès à la ressource, même en l'absence de blocage.
ANNEXE A - SUJETS EN CONCURRENCE
Exemple de blocage
Si les deux processus définissent leurs indicateurs sur true avant que l'un ou l'autre n'ait exécuté l'instruction while, chacun pensera que l'autre est entré dans sa section critique, ce qui provoque un blocage.
/* PROCESS 0 */
flag[0] = true;
while (flag[1])
/* do nothing */;
/* critical section*/;
flag[0] = false;
/* PROCESS 1 */
flag[1] = true;
while (flag[0])
/* do nothing */;
/* critical section*/;
flag[1] = false;
Exemple Livelock
/* PROCESS 0 */
flag[0] = true;
while (flag[1]){
flag[0] = false;
/*delay */;
flag[0] = true;
}
/*critical section*/;
flag[0] = false;
/* PROCESS 1 */
flag[1] = true;
while (flag[0]) {
flag[1] = false;
/*delay */;
flag[1] = true;
}
/* critical section*/;
flag[1] = false;
[...] considérons la séquence d'événements suivante:
Cette séquence pourrait être prolongée indéfiniment et aucun processus ne pourrait entrer dans sa section critique. À proprement parler, il s'agit de pas de blocage, car toute modification de la vitesse relative des deux processus briserait ce cycle et permettrait de pénétrer dans la section critique. Cette condition est appelée livelock. Rappelez-vous que le blocage se produit lorsqu'un ensemble de processus souhaite entrer dans leurs sections critiques mais qu'aucun processus ne peut réussir. Avec livelock, il y a des séquences possibles d'exécution qui réussissent, mais il est également possible de décrire une ou plusieurs séquences d'exécution dans lesquelles aucun processus ne pénètre jamais dans sa section critique.
DEADLOCK Le blocage est une condition dans laquelle une tâche attend indéfiniment des conditions qui ne peuvent jamais être satisfaites - la tâche revendique un contrôle exclusif sur les ressources partagées - la tâche détient les ressources en attendant la libération d'autres ressources - les tâches ne peuvent pas être obligé de séparer les ressources - une condition d'attente circulaire existe
LIVELOCK Des conditions Livelock peuvent survenir lorsque deux tâches ou plus dépendent de la ressource et utilisent cette ressource, ce qui crée une condition de dépendance circulaire dans laquelle ces tâches continuent de s'exécuter à tout jamais, bloquant ainsi l'exécution de toutes les tâches de niveau de priorité inférieur. tâches prioritaires rencontrent une condition appelée famine)
Peut-être que ces deux exemples vous illustrent la différence entre une impasse et un livelock:
Exemple Java pour une impasse:
import Java.util.concurrent.locks.Lock;
import Java.util.concurrent.locks.ReentrantLock;
public class DeadlockSample {
private static final Lock lock1 = new ReentrantLock(true);
private static final Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
Thread threadA = new Thread(DeadlockSample::doA,"Thread A");
Thread threadB = new Thread(DeadlockSample::doB,"Thread B");
threadA.start();
threadB.start();
}
public static void doA() {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
lock1.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
lock2.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
}
public static void doB() {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
lock2.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
lock1.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
}
}
Exemple de sortie:
Thread A : waits for lock 1
Thread B : waits for lock 2
Thread A : holds lock 1
Thread B : holds lock 2
Thread B : waits for lock 1
Thread A : waits for lock 2
Exemple Java pour un livelock:
import Java.util.concurrent.locks.Lock;
import Java.util.concurrent.locks.ReentrantLock;
public class LivelockSample {
private static final Lock lock1 = new ReentrantLock(true);
private static final Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
Thread threadA = new Thread(LivelockSample::doA, "Thread A");
Thread threadB = new Thread(LivelockSample::doB, "Thread B");
threadA.start();
threadB.start();
}
public static void doA() {
try {
while (!lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
while (!lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} catch (InterruptedException e) {
// can be ignored here for this sample
}
}
public static void doB() {
try {
while (!lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
while (!lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} catch (InterruptedException e) {
// can be ignored here for this sample
}
}
}
Exemple de sortie:
Thread B : holds lock 2
Thread A : holds lock 1
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
...
Les deux exemples forcent les fils à acquérir les serrures dans des ordres différents. Alors que l'impasse attend l'autre verrou, le livelock n'attend pas vraiment - il essaie désespérément de se procurer le verrou sans avoir la chance de l'obtenir. Chaque essai consomme des cycles du processeur.