2010-04-06 7 views
6

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 

Respuesta

2

Close es un medio de disp La aparición del objeto (Close y Dispose en esta clase produce un comportamiento idéntico). No afecta el estado del mango. Suponer que, en todos los casos, el usuario desearía que un subproceso en espera en un identificador que cerré para continuar no parezca razonable. De hecho, el hecho de que el identificador sea en uso indica que no debe llamar al Close en primer lugar.

No es una cuestión de "¿por qué no debería ser llamado Set implícitamente?", Se trata de un problema conceptual: Si llama Close, usted ya no debe preocuparse por el objeto. Use Set y Reset para controlar el flujo de ejecución entre hilos; no llame al Close (o Dispose) en ningún objeto, WaitHandle incluido, hasta que ya no estén en uso.

+0

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

+0

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. –

+0

@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'. –

1

Estos eventos de sincronización se basan en los identificadores de espera Win32, y el método Close() solo los libera (como Dispose()) sin señalización, y los subprocesos en espera permanecen en espera.

Cuestiones relacionadas