Entiendo qué es Livelock, pero me preguntaba si alguien tenía un buen ejemplo basado en código. Y por código, no me refiero a "dos personas que intentan cruzarse en un corredor". Si leo eso otra vez, perderé mi almuerzo.
concurrency
livelock
Alex Miller
fuente
fuente
Respuestas:
Aquí hay un ejemplo muy simple de Java de livelock donde un esposo y una esposa están tratando de comer sopa, pero solo tienen una cuchara entre ellos. Cada cónyuge es demasiado educado y pasará la cuchara si el otro aún no ha comido.
fuente
getOwner
tiene que sincronizarse también el método? Desde Java efectivo "la sincronización no tiene efecto a menos que tanto leer como escribir ".Thread.join()
lugar de hacerloThread.sleep()
, porque quiere esperar a que el cónyuge coma?getOwner
método debe estar sincronizado ya que incluso sisetOwner
está sincronizado, esto no garantiza que el hilo que usegetOwner
(o que acceda al campoowner
directamente) vea los cambios realizados por el otro hilosetOwner
. Este video explica esto con mucho cuidado: youtube.com/watch?v=WTVooKLLVT8synchronized
palabras clave para elsetOwner
método, porque leer y escribir es una acción atómica para la variable de referencia.Dejando los comentarios al margen, un ejemplo que se sabe que aparece es en el código que intenta detectar y manejar situaciones de punto muerto. Si dos hilos detectan un punto muerto e intentan "hacerse a un lado" el uno para el otro, sin cuidado, terminarán atrapados en un bucle siempre "haciendo a un lado" y nunca logrando avanzar.
Por "hacer a un lado" quiero decir que liberarían la cerradura e intentarían que el otro la adquiriera. Podemos imaginar la situación con dos hilos haciendo esto (pseudocódigo):
Dejando a un lado las condiciones de carrera, lo que tenemos aquí es una situación en la que ambos hilos, si entran al mismo tiempo, terminarán corriendo en el bucle interno sin continuar. Obviamente este es un ejemplo simplificado. Una solución ingenua sería poner algún tipo de aleatoriedad en la cantidad de tiempo que esperarían los hilos.
La solución adecuada es respetar siempre la jerarquía de bloqueo . Elija un orden en el que adquiera los bloqueos y manténgalo. Por ejemplo, si ambos subprocesos siempre adquieren lock1 antes de lock2, entonces no hay posibilidad de punto muerto.
fuente
Como no hay una respuesta marcada como respuesta aceptada, he intentado crear un ejemplo de bloqueo en vivo;
El programa original fue escrito por mí en abril de 2012 para aprender varios conceptos de subprocesamiento múltiple. Esta vez lo he modificado para crear un punto muerto, condición de carrera, livelock, etc.
Entonces, primero comprendamos el enunciado del problema;
Problema de Cookie Maker
Hay algunos contenedores de ingredientes: ChocoPowederContainer , WheatPowderContainer . CookieMaker toma una cierta cantidad de polvo de los recipientes de ingredientes para hornear una cookie . Si un fabricante de galletas encuentra un contenedor vacío, busca otro contenedor para ahorrar tiempo. Y espera hasta que Filler llene el recipiente requerido. Hay un relleno que revisa el contenedor a intervalos regulares y llena cierta cantidad si un contenedor lo necesita.
Por favor revise el código completo en github ;
Déjame explicarte brevemente la implementación.
Echemos un vistazo al código:
CookieMaker.java
IngredientContainer.java
Todo funciona bien hasta que llene está llenando los contenedores. Pero si me olvido de iniciar el relleno, o si el relleno se va con una licencia inesperada, los subprocesos siguen cambiando sus estados para permitir que otro fabricante vaya y revise el contenedor.
También he creado un demonio ThreadTracer que vigila los estados de hilos y puntos muertos. Esta es la salida de la consola;
Notarás que los subprocesos y cambian sus estados y esperan.
fuente
Un ejemplo real (aunque sin código exacto) es el bloqueo en vivo de dos procesos competidores en un intento de corregir un punto muerto del servidor SQL, y cada proceso utiliza el mismo algoritmo de espera-reintento para volver a intentarlo. Si bien es la suerte del momento, he visto que esto sucede en máquinas separadas con características de rendimiento similares en respuesta a un mensaje agregado a un tema de EMS (por ejemplo, guardar una actualización de un gráfico de un solo objeto más de una vez) y no poder controlar La orden de bloqueo.
Una buena solución en este caso sería tener consumidores competidores (evitar el procesamiento duplicado lo más arriba posible en la cadena al dividir el trabajo en objetos no relacionados).
Una solución menos deseable (ok, truco sucio) es romper la mala suerte del tiempo (tipo de diferencias de fuerza en el procesamiento) por adelantado o romperla después del punto muerto mediante el uso de diferentes algoritmos o algún elemento de aleatoriedad. Esto aún podría tener problemas porque es posible que la orden de bloqueo sea "pegajosa" para cada proceso, y esto lleva un cierto mínimo de tiempo que no se tiene en cuenta en el reintento de espera.
Otra solución más (al menos para SQL Server) es probar un nivel de aislamiento diferente (por ejemplo, una instantánea).
fuente
Codifiqué el ejemplo de 2 personas que pasaban por un pasillo. Los dos hilos se evitarán en cuanto se den cuenta de que sus direcciones son las mismas.
fuente
Versión C # del código de jelbourn:
fuente
Un ejemplo aquí podría ser usar un tryLock cronometrado para obtener más de un bloqueo y, si no puede obtenerlos todos, retroceda e intente nuevamente.
Me imagino que dicho código sería problemático ya que tiene muchos hilos colisionando y esperando obtener un conjunto de bloqueos. Pero no estoy seguro de que esto sea muy convincente para mí como un simple ejemplo.
fuente
tryLockAll()
con los bloqueoslocks
en el mismo orden, no hay livelock.Versión de Python del código de jelbourn:
fuente
Modifico la respuesta de @jelbourn. Cuando uno de ellos se da cuenta de que el otro tiene hambre, él (ella) debe soltar la cuchara y esperar otra notificación, para que ocurra un bloqueo en vivo.
fuente
fuente