Nos sorprendió saber hoy que los hilos que esperan en un ManualResetEvent
continúan esperando el evento incluso cuando está cerrado. Hubiéramos esperado que al llamar al Close()
se señalaran implícitamente los hilos de espera.¿Por qué los subprocesos que esperan en un ManualResetEvent continúan esperando incluso cuando se llama a Close()?
Hemos rastreado esto como una razón por la que algunos de nuestros servicios de Windows no se cerraban tan rápido como nos gustaría. Estamos cambiando todas nuestras implementaciones Dispose
que cierran las referencias ManualResetEvent
para llamar primero a Set
.
¿Alguien puede explicar por qué Close
no llama implícitamente Set
? ¿Cuándo quieres que un hilo de espera continúe esperando?
Aquí es nuestro código de prueba para demostrar nuestros hallazgos:
private static readonly Stopwatch _timer = Stopwatch.StartNew();
public static void Test()
{
var sync = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(state =>
{
Log("ThreadPool enter, waiting 250ms...");
sync.WaitOne(250);
Log("ThreadPool exit");
});
Log("Main sleeping 100");
Thread.Sleep(100);
Log("Main about to close");
// sync.Set(); // Is Set called implicitly? No...
sync.Close();
Log("Main waiting for exit 500ms");
Thread.Sleep(500);
}
private static void Log(string text)
{
Console.WriteLine("{0:0} {1}", _timer.ElapsedMilliseconds, text);
}
Cuando ejecutamos este código con la llamada Set
comentó, obtenemos este ..
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
103 Main about to close
103 Main waiting for exit 500ms
259 ThreadPool exit
Cuando explícitamente llamamos Set
obtenemos esto ..
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
98 Main about to close
98 ThreadPool exit
98 Main waiting for exit 500ms
Para agregar a eso, sería difícil rastrear las condiciones de carrera, incluso en el ejemplo del OP, tendría problemas si sync.Close(); pasó a llamarse antes de que el grupo de subprocesos hiciera sync.WaitOne(); – nos
El problema que tengo con todo esto es que un WaitHandle está diseñado para admitir la sincronización entre subprocesos, por lo que siento que Set on Close y el evento Wait after Close deberían manejarse implícitamente. Sin embargo, sin embargo, creamos una clase extendida personalizada que agrega la funcionalidad que queremos. –
@Sam: me alegra que hayas encontrado una solución, pero, de nuevo, el problema es que tu filosofía sobre la duración del objeto no coincide con la de los diseñadores: el mero hecho de que * cuidas * sobre en qué estado está el identificador significa que usted, por su diseño, aún no está listo para llamar 'Cerrar'. –