2013-08-29 20 views
5

Sto usando un'applicazione che sincronizza i thread usando ManualResetEvent. FxCop mi ha detto di eliminare quegli oggetti. Ho trovato la seguente discussione, che mi ha detto la stessa:Quando deve essere eliminato un oggetto ManualResetEvent?

Do I need to Dispose() or Close() an EventWaitHandle?

Ma io non so quando di disporre un'istanza di un ManualResetEvent.

Il seguente codice semplificato illustra il problema:

private void btn_Click(object sender, EventArgs e) 
{ 
    var mre = new ManualResetEvent(false); 
    new Thread(() => this.SetEvent(mre)).Start(); 
    for (int i = 0; i < 10; ++i) 
    { 
     new Thread(() => this.WaitFor(mre)).Start(); 
    } 
} 

private void SetEvent(ManualResetEvent manualResetEvent) 
{ 
    Thread.Sleep(10000); 
    manualResetEvent.Set(); 
} 

private void WaitFor(ManualResetEvent manualResetEvent) 
{ 
    manualResetEvent.WaitOne(); 
} 

Il problema è che non esistono più istanze del ManualResetEvent e più thread sono in attesa per ogni istanza.

Se memorizzo le istanze in un elenco, non so quando disporlo. Smaltendolo dopo WaitOne() - la chiamata lo smaltirà più volte e forse verrà eliminato mentre altri thread sono ancora in attesa.

Il thread che ha creato l'evento non ha alcun riferimento ad esso. Il thread setter non dovrebbe eliminarlo perché ci sono altri thread che attendono questo MRE. Ogni thread in attesa non è in grado di eliminarlo come menzionato prima.

Quindi la domanda è: quando questo ManualResetEvent deve essere smaltito?

+1

Il codice non ha senso e c'è solo * una * istanza di MRE. Smaltirlo alla fine del metodo va bene. –

+1

No, non penso.Il codice che crea il MRE è un gestore di eventi click del pulsante. Puoi fare clic più volte per creare più MRE. Il codice sopra è semplificato. Smaltire il MRE alla fine di questo metodo non funzionerà perché ci sono 11 thread che stanno usando questo MRE dopo che il metodo è stato completato. Il thread setter attende 10 secondi e altri 10 thread attendono questo MRE. –

risposta

5

Il ManualResetEvent deve essere smaltito quando non è più necessario. La tua vera domanda è: "come faccio a sapere che non ne ho più bisogno?"

In genere viene notificato qualcosa quando si eseguono i thread e si chiama Join sulla discussione. Oppure il thread imposta qualche evento per indicare che è finito. Se hai più thread, tutti possono segnalare uno CountdownEvent. Esistono molti altri modi per gestire la notifica dei thread.

Il punto è che se si assegnano risorse, è compito dell'utente assicurarsi che siano disposte correttamente. Nel tuo codice qui sopra, non hai modo di tenere traccia di quali thread sono in esecuzione o quali thread sono associati a quale ManualResetEvent. Se vuoi assicurarti che MRE sia smaltito correttamente, devi tracciarlo, non solo tenere traccia del MRE ma anche quali thread lo stanno usando, quali thread hanno completato il loro lavoro, e ricevere una notifica quando tutti i thread i thread sono fatti in modo da poter disporre di cose.

Nella tua situazione particolare, se davvero dovessi usare un MRE in questo modo, probabilmente creerei una struttura dati che contiene riferimenti ai thread e al MRE, e un CountdownEvent che i thread segnalano quando hanno finito. Qualcosa di simile:

class WorkUnit 
{ 
    public List<Thread> Threads; 
    public ManualResetEvent MRE; 
    public CountdownEvent CE; 
} 

Ora, quando un thread termina lo fa:

workUnit.CE.Signal(); 

qualche altra parte del vostro programma (probabilmente il thread principale) controlla l'elenco delle unità di lavoro periodicamente. Per ogni elemento in tale elenco lo fa:

if (workUnit.CE.WaitOne(0)) 
{ 
    foreach (Thread t in workUnit.Threads) 
    { 
     t.Join(); 
    } 
    // dispose the MRE and the CE 
    // and remove the work unit from the list 
} 

Sì, è un sacco di lavoro. Probabilmente è meglio se puoi strutturare il tuo programma in modo da non dover fare questo genere di cose.

+0

Grazie Jim. L'unico problema è che non so quanti thread attenderanno il MRE ed è per questo che non posso inizializzare un CountDownEvent. Ma hai ragione che dovrei considerare un progetto che sappia quando l'MRE non è più necessario. Attualmente non ci sono thread in attesa della fine dei thread di lavoro perché calcolano alcuni valori e visualizzano il risultato nei controlli. Dopo di ciò il compito (oh sì sto usando task anziché thread ma penso che il problema sia sempre lo stesso perché un task può impostare CountDownEvent usando ContinueWith) finisce e non c'è codice che lo aspetta esplicitamente. –

+0

Ho trovato una soluzione per i miei casi. Il setter-thread viene creato e avviato in un solo metodo che restituisce l'MRE. Il chiamante di questo metodo conosce MRE e lo diffonde a più thread ma conosce i destinatari. Sono in grado di avvolgere il MRE in un oggetto gestore che gestisce i ricevitori MRE e aspettare che finisca lì. Dopo che il MRE potrebbe essere rilasciato. Grazie per l'aiuto. –

Problemi correlati