2010-02-10 27 views
14

Ho letto su .NET Threading e stavo lavorando su un codice che utilizza uno ManualResetEvent. Ho trovato molti esempi di codice su internet. Tuttavia, quando si legge la documentazione per WaitHandle, ho visto la seguente:Devo chiamare Close() su un ManualResetEvent?

WaitHandle implementa il pattern Dispose . Vedere Implementazione finalizzata e Dispose to Clean Up Unmanaged Risorse.

Nessuno dei campioni sembrano chiamare .Close() sugli oggetti che essi creano ManualResetEvent, anche il bel Recursion and Concurrency articolo dal blog pfxteam (Modifica - questo ha un blocco utilizzando I ha perso). Questo è solo un esempio di supervisione, o non è necessario? Sono curioso perché un WaitHandle "incapsula gli oggetti specifici del sistema operativo", quindi potrebbe facilmente esserci una perdita di risorse.

risposta

11

In generale, se un oggetto implementa IDisposable lo fa per un motivo e si deve chiamare Dispose (o Close, a seconda dei casi). Nell'esempio del sito, ManualResetEvent è racchiuso all'interno di un'istruzione using che gestirà "automaticamente" la chiamata Dispose. In questo caso, Close è sinonimo di Dispose (che è vero nella maggior parte delle implementazioni IDisposable che forniscono un metodo Close).

Il codice dall'esempio:

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

espande a

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

Noterete il codice

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(); 
} 

utilizza il 'usando' parola chiave. In questo modo, il metodo di smaltimento viene chiamato automaticamente anche se il codice genera un'eccezione.

+0

Ho assolutamente perso il blocco utilizzando quando si guarda oltre quel codice. Grazie per segnalarlo. –

2

ho usato ManualResetEvent molto e non credo che abbia mai usato all'interno di un singolo method-- è sempre un campo di istanza di una classe. Pertanto, using() spesso non si applica.

Se si dispone di un campo istanza di classe che è un'istanza di ManualResetEvent, rendere la vostra classe di implementare IDisposable e nel metodo Dispose() chiamata ManualResetEvent.Close(). Quindi, in tutti gli usi della classe, è necessario utilizzare using() oppure effettuare l'implementazione della classe contenente IDisposable e ripetere e ripetere ...

2

Se si utilizza uno ManualResetEvent con metodi anonimi, è ovviamente utile. Ma come Sam ha detto che possono spesso essere passati in giro per i lavoratori, e quindi impostare e chiudere.

Quindi direi che dipende dal contesto in cui lo si utilizza: l'esempio di codice the MSDN WaitHandle.WaitAll() ha un buon esempio di cosa intendo.

Ecco un esempio basato sul campione MSDN di come creare le WaitHandles con una dichiarazione using farebbe un'eccezione:

System.ObjectDisposedException
"maniglia di sicurezza è stato chiuso"

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

Questo sembra essere un esempio in cui .Close() non viene chiamato su ManualResetEvent e non è possibile utilizzare il blocco. Non penso che l'operatore possa chiuderlo dopo un set perché il thread principale lo sta usando nella chiamata WaitHandle.WaitAll (manualEvents). –

+0

@Kevin mio punto era la matrice di WaitHandles non può essere avvolto in una clausola using quando viene creato come * Penso che * sarebbero chiusi quando sono arrivati, avrei bisogno di controllare però. –

17

Sono stato recentemente inoltrato un estratto da C# 4.0 in a Nutshell: The Definitive Reference di Joseph Albahari, Ben Albahar io. Nella pagina 834, in capitolo 21: Threading c'è una sezione che parla di questo.

Smaltimento Attendere Maniglie

Una volta che hai finito con un manico di attesa , è possibile chiamare il suo Chiudi metodo di rilasciare il sistema operativo risorsa. In alternativa, è possibile semplicemente rilasciare tutti i riferimenti alla maniglia attesa e consentire al garbage collector per fare il lavoro per voi qualche tempo dopo (attendere maniglie implementare il modello di smaltimento cui il finalizzatore chiama Chiudi). Questo è uno dei pochi scenari in cui fare affidamento su questo backup è (discutibilmente) accettabili, perché aspettare maniglie hanno un sistema operativo a carico leggero (delegati asincroni si basano su esattamente questo meccanismo per rilasciare loro di IAsyncResult maniglia attesa).

Le maniglie di attesa vengono rilasciate automaticamente quando un dominio dell'applicazione scarica.

+0

La documentazione di WaitHandle.Finalize dice che non esiste più un'implementazione da .NET 2.0. Puoi anche vederlo con un decompilatore. WaitHandle non ha più un finalizzatore. Non so perché, ma ogni Waithamand abbandonato trapelerà sembra. Vedi http://msdn.microsoft.com/en-us/library/vstudio/bb291974(v=vs.90).aspx – Djof

+4

@Djof: anche se 'WaitHandle' non ha più un metodo' Finalizza', non lo faccio ' Penso che significhi che perdono. Invece, la pulizia viene gestita all'interno di un 'SafeHandle' a cui un' WaitHandle' contiene un riferimento. – supercat