Estoy teniendo dificultades para entender Wait()
, Pulse()
, PulseAll()
. ¿Todos ellos evitarán un punto muerto? Te agradecería si explicas cómo usarlos?C#: Monitor - Espera, pulso, PulseAll
Respuesta
versión corta:
lock(obj) {...}
es corto la mano para Monitor.Enter
/Monitor.Exit
(con gastos de envío, etc. excepción). Si nadie más tiene el bloqueo, puede obtenerlo (y ejecutar su código); de lo contrario, su hilo quedará bloqueado hasta que se adquiera el bloqueo (mediante otro hilo que lo suelte).
punto muerto que suele ocurrir cuando cualquiera de R: dos hilos se bloquean las cosas en diferentes órdenes:
thread 1: lock(objA) { lock (objB) { ... } }
thread 2: lock(objB) { lock (objA) { ... } }
(aquí, si cada uno de ellos adquieren la primera cerradura, y no las puede vez conseguir el segundo, ya que ni el hilo puede salir para liberar su bloqueo)
Este escenario se puede minimizar siempre bloqueando en el mismo orden; y puede recuperar (hasta cierto punto) utilizando Monitor.TryEnter
(en lugar de Monitor.Enter
/lock
) y especificando un tiempo de espera.
o B: se puede bloquear a sí mismo con cosas como WinForms cuando hilos de conmutación, mientras que mantiene un bloqueo:
lock(obj) { // on worker
this.Invoke((MethodInvoker) delegate { // switch to UI
lock(obj) { // oopsiee!
...
}
});
}
El punto muerto parece obvia anteriormente, pero no es tan obvio cuando se tiene código espagueti; respuestas posibles: no cambie de hilo mientras mantiene bloqueos, o use BeginInvoke
para que al menos pueda salir del bloqueo (permitiendo que se reproduzca la IU).
Wait
/Pulse
/PulseAll
son diferentes; son para señalización.Yo uso este in this answer para señalar de manera que:
Dequeue
: si intenta quitar de la cola de datos cuando la cola está vacía, se espera a que otro hilo para agregar datos, lo que despierta el hilo bloqueadoEnqueue
: si tratas de datos en cola cuando la cola está llena, se espera a que otro hilo para eliminar los datos, lo que despierta el hilo bloqueado
Pulse
sólo se despierta uno hilo - pero no soy lo suficientemente inteligente para probar que el próximo hilo i s siempre es el que yo quiero, entonces tiendo a usar PulseAll
, y simplemente vuelvo a verificar las condiciones antes de continuar; como ejemplo:
while (queue.Count >= maxSize)
{
Monitor.Wait(queue);
}
Con este enfoque, puedo agregar con seguridad otros significados de Pulse
, sin mi código existente asumiendo que "me desperté, por lo tanto, no hay datos" - lo cual es útil cuando (en el mismo ejemplo) Más tarde tuve que agregar un método Close()
.
Lea Jon Skeet's multi-part threading article.
Es realmente bueno. Las que menciona son aproximadamente un tercio del camino.
No, no lo protegen de interbloqueos. Son herramientas más flexibles para la sincronización de hilos. Aquí hay una muy buena explicación sobre cómo usarlos y un patrón de uso muy importante: sin este patrón, romperá todas las cosas: http://www.albahari.com/threading/part4.aspx
PARA NOVICES ¡RECOMENDAMOS ALTAMENTE que la página de albahari sea perfecta! Pasa por el proceso de subprocesamiento y la sincronización paso a paso con ejemplos claros. – DanG
Son herramientas para sincronizar y señalizar entre hilos. Como tales, no hacen nada para evitar interbloqueos, pero si se utilizan correctamente, se pueden usar para sincronizar y comunicarse entre hilos.
Desafortunadamente, la mayor parte del trabajo necesario para escribir el código correcto multiproceso es actualmente responsabilidad de los desarrolladores en C# (y en muchos otros lenguajes). Eche un vistazo a cómo F #, Haskell y Clojure manejan esto para un enfoque completamente diferente.
Receta simple para usar Monitor.Wait y Monitor.Pulse. Se compone de un trabajador, un jefe y un teléfono que utilizan para comunicarse:
object phone = new object();
un "trabajador" hilo:
lock(phone) // Sort of "Turn the phone on while at work"
{
while(true)
{
Monitor.Wait(phone); // Wait for a signal from the boss
DoWork();
Monitor.PulseAll(phone); // Signal boss we are done
}
}
un "jefe" hilo:
PrepareWork();
lock(phone) // Grab the phone when I have something ready for the worker
{
Monitor.PulseAll(phone); // Signal worker there is work to do
Monitor.Wait(phone); // Wait for the work to be done
}
Siguen ejemplos más complejos ...
A "Trabajador con otra cosa que hacer":
lock(phone)
{
while(true)
{
if(Monitor.Wait(phone,1000)) // Wait for one second at most
{
DoWork();
Monitor.PulseAll(phone); // Signal boss we are done
}
else
DoSomethingElse();
}
}
Un "Impaciente Boss":
PrepareWork();
lock(phone)
{
Monitor.PulseAll(phone); // Signal worker there is work to do
if(Monitor.Wait(phone,1000)) // Wait for one second at most
Console.Writeline("Good work!");
}
No lo entiendo ¿Cómo es posible que Boss y Worker estén bloqueados "por teléfono" al mismo tiempo? – Marshall
@Marshall Monitor.Wait libera el bloqueo del "teléfono" al siguiente en la línea (presumiblemente al Jefe). –
@DennisGorelik ahh, ya veo. He ampliado su punto [a continuación] (http://stackoverflow.com/a/42581381/1282864) – jdpilgrim
Desafortunadamente, ninguno de espera(), Pulso() o PulseAll() tiene la propiedad mágica que se desean para - y es que por utilizando este API evitará automáticamente el punto muerto.
considere el siguiente código
object incomingMessages = new object(); //signal object
LoopOnMessages()
{
lock(incomingMessages)
{
Monitor.Wait(incomingMessages);
}
if (canGrabMessage()) handleMessage();
// loop
}
ReceiveMessagesAndSignalWaiters()
{
awaitMessages();
copyMessagesToReadyArea();
lock(incomingMessages) {
Monitor.PulseAll(incomingMessages); //or Monitor.Pulse
}
awaitReadyAreaHasFreeSpace();
}
Este código será un punto muerto! Quizás no hoy, quizás no mañana. Lo más probable es que cuando su código esté bajo estrés, de repente se haya vuelto popular o importante, y lo llamen para solucionar un problema urgente.
¿Por qué?
Finalmente, ocurrirá lo siguiente:
- Todas las roscas de los consumidores están haciendo un trabajo
- Los mensajes llegan, el área lista no puede contener más mensajes, y PulseAll() se llama.
- Ningún consumidor se despertó, porque ninguno está esperando
- Todas las roscas de consumo Llam.Esper() [DEADLOCK]
Este ejemplo particular asume que el hilo productor nunca se va a llamar PulseAll() de nuevo porque no tiene más espacio para colocar mensajes. Pero hay muchas, muchas variaciones rotas en este código. La gente va a tratar de hacerlo más robusto cambiando una línea como la fabricación de Monitor.Wait();
en
if (!canGrabMessage()) Monitor.Wait(incomingMessages);
Por desgracia, que todavía no es suficiente para solucionarlo. Para solucionarlo se también necesidad de modificar el ámbito de bloqueo, donde Monitor.PulseAll()
se llama:
LoopOnMessages()
{
lock(incomingMessages)
{
if (!canGrabMessage()) Monitor.Wait(incomingMessages);
}
if (canGrabMessage()) handleMessage();
// loop
}
ReceiveMessagesAndSignalWaiters()
{
awaitMessagesArrive();
lock(incomingMessages)
{
copyMessagesToReadyArea();
Monitor.PulseAll(incomingMessages); //or Monitor.Pulse
}
awaitReadyAreaHasFreeSpace();
}
El punto clave es que en el código fijo, las cerraduras restringen las posibles secuencias de eventos:
- A
hilos de consumo hace su trabajo y bucles
Ese hilo adquiere el bloqueo
Y gracias al bloqueo ahora es cierto que :
a. Mensajes tienen todavía no llegó a la zona preparada, y se libera el bloqueo llamando Espera() antes de la rosca receptor del mensaje puede adquirir el bloqueo y la copia Más mensajes en el área de lista, o
b. Los mensajes tienen ya llegados en el área de listo y recibe los mensajes EN LUGAR DE llamar a Wait(). (Y si bien es tomar esta decisión es imposible que el hilo receptor del mensaje a, por ejemplo adquirir el bloqueo y la copia más mensajes a la zona preparada.)
Como resultado, el problema del código original ahora nunca ocurre : 3. Cuando PulseEvent() se llama Ningún consumidor se despertó, porque ninguno está esperando
Ahora observar que en este código que tiene que conseguir el alcance de cierre exactamente a la derecha. (Si, de hecho lo hizo bien!)
Y también, ya que se debe utilizar el lock
(o Monitor.Enter()
etc.) con el fin de utilizar Monitor.PulseAll()
o Monitor.Wait()
de una manera libre de punto muerto, todavía tiene que preocuparse por la posibilidad de otros bloqueos que suceden debido a ese bloqueo.
En pocas palabras: estas API también son fáciles de meter la pata y el punto muerto con, es decir, bastante peligroso
Algo que me totales arrojó aquí es que Pulse
simplemente da un "mano a mano" con un hilo en un Wait
.El subproceso Esperando no continuará hasta que el subproceso que hizo Pulse
abandone el bloqueo y el subproceso de espera lo gane satisfactoriamente.
lock(phone) // Grab the phone
{
Monitor.PulseAll(phone); // Signal worker
Monitor.Wait(phone); // ****** The lock on phone has been given up! ******
}
o
lock(phone) // Grab the phone when I have something ready for the worker
{
Monitor.PulseAll(phone); // Signal worker there is work to do
DoMoreWork();
} // ****** The lock on phone has been given up! ******
En ambos casos no es hasta que "la cerradura en el teléfono se ha renunciado a" otro hilo que puede conseguirlo.
Puede haber otros subprocesos esperando ese bloqueo de Monitor.Wait(phone)
o lock(phone)
. Solo el que gane el candado podrá continuar.
- 1. monitor vs objeto mutex en C#
- 2. Aplicación C++ para desconectar el monitor secundario
- 3. ¿Cómo puedo codificar un monitor en C?
- 4. ¿Cómo activo un segundo monitor en C#?
- 5. Detectar si el monitor está en C#
- 6. Diferencia entre monitor y bloqueo?
- 7. Encender/apagar el monitor
- 8. SpinWait vs espera en espera. ¿Cuál usar?
- 9. C#: Nombre del método espera
- 10. Monitor de red por proceso en Obj-C?
- 11. ¿Cómo crear un monitor/bloqueador simple C# http?
- 12. monitor de acceso a archivos/directorios en C#
- 13. MBean Simple Graph Monitor
- 14. monitor de impresión
- 15. Monitor de red gratuito
- 16. commit monitor para tfs
- 17. Monitor de cookies Jquery
- 18. Monitor Network Traffic Mac
- 19. .NET Process Monitor
- 20. Monitor IIS 6.0
- 21. Monitor de RSS específica
- 22. Formulario HTML: cuando pulso enter, ¡refresca la página!
- 23. WPF botón de activación del pulso vez sin control
- 24. Cómo copiar celdas entre tablas como la aplicación de pulso
- 25. Colas de espera y colas en C#
- 26. C#: Tiempo de espera en SerialPort.Open?
- 27. Implemente el tiempo de espera de C#
- 28. Tiempo de espera del subproceso en C#
- 29. Estadísticas/Monitor/Inspector para beanstalkd
- 30. GIT Log o Commit Monitor
ni siquiera menciona Pulso o ¡Espera! Vincular a un artículo de John Skeet no hace que una respuesta sea buena de manera automática ... –
¿De verdad? ¿Qué es esto entonces? 'http: // www.yoda.arachsys.com/csharp/threads/deadlocks.shtml' – Geo
@Geo: sí, este encaja mejor;) –