2010-02-10 18 views
14

He estado leyendo sobre .NET Threading y estaba trabajando en algún código que usa un ManualResetEvent. He encontrado muchos ejemplos de código en internet. Sin embargo, al leer la documentación de WaitHandle, vi lo siguiente:¿Debo llamar a Close() en un evento ManualReset?

WaitHandle implementa el patrón Desechar . Consulte la Implementación de Finalizar y Eliminar para limpiar los recursos no administrados.

Ninguna de las muestras parece que llamar .Close() en los objetos ManualResetEvent que crean, incluso el buen Recursion and Concurrency artículo del blog pfxteam (Editar - esto tiene un bloque mediante I ha perdido) ¿Es solo un ejemplo de supervisión o no es necesario? Tengo curiosidad porque un WaitHandle "encapsula objetos específicos del sistema operativo", por lo que fácilmente podría haber una fuga de recursos.

Respuesta

11

En general, si un objeto implementa IDisposable lo hace por algún motivo y debe llamar al Dispose (o Close, según sea el caso). En el sitio de ejemplo, el evento ManualResetEvent está envuelto dentro de una declaración using, que manejará "automáticamente" llamando al Dispose. En este caso, Close es sinónimo de Dispose (que es cierto en la mayoría de las implementaciones IDisposable que proporcionan un método Close).

El código del ejemplo:

using (var mre = new ManualResetEvent(false)) 
{ 
    ... 
} 

expande a

var mre = new ManualResetEvent(false); 
try 
{ 
    ... 
} 
finally 
{ 
    ((IDispoable)mre).Dispose(); 
} 
2

Se dará cuenta el código

using (var mre = new ManualResetEvent(false)) 
{ 
    // Process the left child asynchronously 
    ThreadPool.QueueUserWorkItem(delegate 
    { 
     Process(tree.Left, action); 
     mre.Set(); 
    }); 

    // Process current node and right child synchronously 
    action(tree.Data); 
    Process(tree.Right, action); 

    // Wait for the left child 
    mre.WaitOne(); 
} 

utiliza el 'usar' palabras clave. Esto llama automáticamente al método de eliminación cuando finaliza, incluso si el código arroja una excepción.

+0

Eché de menos totalmente el bloque de uso cuando revisé ese código. Gracias por mencionarlo. –

2

He usado ManualResetEvent mucho y no creo que jamás he utilizado dentro de una sola method-- siempre es un campo de instancia de una clase. Por lo tanto, using() a menudo no se aplica.

Si usted tiene un campo de instancia de clase que es una instancia de ManualResetEvent, hacer que su clase implemente IDisposable y en su Dispose() llamada al método ManualResetEvent.Close(). Luego, en todos los usos de su clase, es necesario utilizar using() o hacer la clase que contiene implementar IDisposable y repetir, y repetir ...

2

Si estás utilizando un ManualResetEvent con métodos anónimos entonces es obviamente útil. Pero como Sam mencionó, a menudo se les puede pasar a los trabajadores, y luego establecerlos y cerrarlos.

Así que yo diría que depende del contexto de cómo lo está usando - the MSDN WaitHandle.WaitAll() ejemplo de código tiene un buen ejemplo de lo que quiero decir.

Aquí es un ejemplo basado en la muestra de MSDN de cómo crear las WaitHandles con una declaración using haría una excepción:

System.ObjectDisposedException
"manija de seguridad se ha cerrado"

const int threads = 25; 

void ManualWaitHandle() 
{ 
    ManualResetEvent[] manualEvents = new ManualResetEvent[threads]; 

    for (int i = 0; i < threads; i++) 
    { 
     using (ManualResetEvent manualResetEvent = new ManualResetEvent(false)) 
     { 
      ThreadPool.QueueUserWorkItem(new WaitCallback(ManualWaitHandleThread), new FileState("filename", manualResetEvent)); 
      manualEvents[i] = manualResetEvent; 
     } 
    } 

    WaitHandle.WaitAll(manualEvents); 
} 

void ManualWaitHandleThread(object state) 
{ 
    FileState filestate = (FileState) state; 
    Thread.Sleep(100); 
    filestate.ManualEvent.Set(); 
} 

class FileState 
{ 
    public string Filename { get;set; } 
    public ManualResetEvent ManualEvent { get; set; } 

    public FileState(string fileName, ManualResetEvent manualEvent) 
    { 
     Filename = fileName; 
     ManualEvent = manualEvent; 
    } 
} 
+0

Parece ser un ejemplo donde .Close() no se llama en el ManualResetEvent, y no hay un bloque de uso. No creo que el trabajador pueda cerrarlo después de un conjunto porque el hilo principal lo está usando en la llamada WaitHandle.WaitAll (manualEvents). –

+0

@Kevin mi punto era que la matriz de WaitHandles no se puede incluir en una cláusula de uso cuando se crea como * Creo * que se cerrarían cuando llegaran, pero necesitaría comprobarlo. –

17

Recientemente recibí un extracto del C# 4.0 in a Nutshell: The Definitive Reference Por Joseph Albahari, Ben Albahar yo. En la página 834, en Capítulo 21: Threading, hay una sección que habla de esto.

Eliminación de espera Maneja

Una vez que haya terminado con un mango de espera , puede llamar a su método Cerrar para liberar el sistema operativo recursos. Como alternativa, puede caer simplemente todas las referencias a la manija de espera y permitir que el recolector de basura para hacer el trabajo para usted en algún momento posterior (manijas esperan implementar el patrón de eliminación mediante el cual el finalizador llama Cerrar). Esta es una de las pocas escenarios en los que confiando en esta copia de seguridad es (posiblemente) aceptable, porque esperar mangos tienen una carga OS luz (delegados asíncronos se basan en exactamente este mecanismo para liberar su mango espera de IAsyncResult).

Se liberan los identificadores de espera automáticamente cuando se descarga una aplicación .

+0

La documentación de WaitHandle.Finalize indica que ya no existe una implementación de .NET 2.0. También puedes ver esto con un descompilador. WaitHandle ya no tiene un finalizador. No sé por qué, pero cualquier WaitHandle abandonado se filtrará parece. Consulte http://msdn.microsoft.com/en-us/library/vstudio/bb291974(v=vs.90).aspx – Djof

+4

@Djof: aunque 'WaitHandle' ya no tiene un método' Finalize', no lo hago. Creo que eso significa que tienen fugas. En cambio, la limpieza se maneja dentro de un 'SafeHandle' al que un' WaitHandle' contiene una referencia. – supercat

Cuestiones relacionadas