2015-08-19 14 views
6

Ho un oggetto che contiene un thread di lavoro. Mi piacerebbe uccidere il thread quando l'oggetto è fuori portata.Come interrompere implicitamente una discussione in un oggetto in C#

using System.IO; 
using System; 
using System.Threading; 

namespace tt { 
    class Program 
    { 
     static void Main() 
     { 
      AnotherClass a = new AnotherClass(); 
      a.Say(); 
     } 

    } 

    class AnotherClass:IDisposable { 
     private bool m_Disposed; 
     private readonly AutoResetEvent m_ResetEvent = new AutoResetEvent(false); 

     public AnotherClass() { 
      Thread t = new Thread(wait); 
      t.Start(); 
     } 

     public void Dispose() { 
      Dispose(true); 
     } 
     private void Dispose(bool disposing) { 
      if (m_Disposed) { 
       return; 
      } 

      if (disposing) { 
       Console.WriteLine("inner disposing"); 
      } 
      m_ResetEvent.Set(); 
      Console.WriteLine("Outer disposing"); 
      m_Disposed = true; 
     } 

     private void wait() { 
      m_ResetEvent.WaitOne(); 
     } 

     ~AnotherClass() { 
      Dispose(false); 
     } 

     public void Say() { 
      Console.WriteLine("HellO"); 
     } 
    } 
} 

Il programma si bloccherà solo perché il distruttore di AnotherClass non viene chiamato. So che posso chiamare a.Dispose() per uccidere il thread. Ma esiste un modo per uccidere implicitamente il thread quando l'oggetto esce dall'oscilloscopio?

+0

Penso che l'unico modo per interrompere implicitamente un thread è disconnettere l'alimentazione. –

risposta

6

No, questo non è possibile. Non c'è un conteggio dei riferimenti che possa notare che l'oggetto non ha più riferimenti, quindi non c'è modo di fare nulla quando ciò accade.

L'interfaccia IDisposable viene utilizzata per le classi che devono fare qualcosa quando un'istanza non verrà più utilizzata, e chiamando il metodo Dispose è come si segnala che si è fatto con l'istanza.

Il solito modo per assicurarsi che un oggetto è disposto quando si lascia un campo di applicazione è quello di avvolgere il codice in un using blocco:

static void Main() 
{ 
    using (AnotherClass a = new AnotherClass()) 
    { 
    a.Say(); 
    } 
} 

Il blocco using è zucchero sintattico per un blocco try...finally:

static void Main() 
{ 
    AnotherClass a = new AnotherClass(); 
    try 
    { 
    a.Say(); 
    } 
    finally 
    { 
    if (a != null) 
    { 
     ((Idisposable)a).Dispose(); 
    } 
    } 
} 
+0

Dei e piccoli pesci, io amo usare ". Ricordare di chiamare 'Dispose' non è un problema, ma" using "sembra davvero dannatamente elegante nel codice. –

+0

quindi se AnotherClass è una libreria, non c'è modo di garantire che l'utente chiuda/disfa correttamente AnotherClass? –

+0

@MonsterHunter Se implementa IDisposable, allora c'è. Se non si ha accesso alla logica nell'altra classe e l'IT non dispone in modo corretto, è possibile esaminare un'altra libreria. – Cory

1

Si potrebbe utilizzare la gestione delle eccezioni e utilizzare l'errore visualizzato come l'eccezione nel blocco catch

2

aggiunta alla risposta di Guffa, penso che sia anche importante notare che anche se si ha un modo per rilevare il momento preciso in cui la tua istanza AnotherClass è uscita dal campo di applicazione, in questo caso, non lo sarà mai. Ed è per questo che hai notato che il tuo distruttore non è mai stato chiamato.

La ragione di questo è che quando si crea e avvia il filo di lavoro, il filo viene passato un riferimento delegato che punta al metodo di istanza wait() che viene fornito con un implicito riferimento alla this (l'istanza AnotherClass). Quindi, fintanto che il thread non esce di per sé, terrà un forte riferimento all'istanza AnotherClass e gli impedirà di raccogliere i dati inutili.

+0

Quindi, come posso assicurarmi che AntherClass non causi il blocco del programma senza toccare il codice nella classe del programma? –

+1

In base alle informazioni disponibili, utilizzare il pattern 'IDisposable' come suggerito da Guffa è il modo migliore per andare. Ma hai anche ragione nel dire che se il chiamante non chiama 'Dispose()', allora non funzionerà. Esistono alcune soluzioni alternative che prevedono l'utilizzo del finalizzatore, ma quelle opzioni raramente sono una buona idea e possono portare ad altri problemi più seri. Se fossi nei tuoi panni, prenderei in considerazione la possibilità di cambiare il mio design di classe. Ma senza sapere quali sono i requisiti aziendali, è difficile suggerire un'alternativa di design migliore. – sstan

1

Se il thread esiste allo scopo di servire un oggetto per il beneficio di altri thread, è necessario disporre di un oggetto wrapper pubblico a cui tutti i thread che sono "interessati" al servizio manterranno forti riferimenti e il servizio stesso manterrà un riferimento lungo e debole. L'oggetto wrapper non deve contenere alcuna informazione che il servizio deve leggere o manipolare, ma deve invece inoltrare tutte le richieste di informazioni a un oggetto che sia il wrapper che il servizio conservino riferimenti forti.

Quando il thread del server è attivo, deve verificare periodicamente se il riferimento debole all'oggetto wrapper è ancora attivo. In caso contrario, il thread del server dovrebbe essere chiuso in modo ordinato. Nota che il server non usa mai un riferimento forte all'oggetto wrapper, nemmeno temporaneamente, quindi l'oggetto wrapper dovrebbe evaporare non appena nessuno è interessato a questo.

Se il thread del server può essere bloccato per un tempo indefinito in attesa che accadano varie cose, il wrapper deve contenere l'unico riferimento in qualsiasi punto dell'universo su un oggetto finalizzabile che contiene un riferimento forte all'oggetto server; quando il finalizzatore si attiva, deve controllare se il riferimento debole lungo detenuto dall'oggetto server è ancora valido. Se non lo è, dovrebbe provare a spostare leggermente il thread del server (ad es.utilizzando un Monitor.TryEnter/Monitor.Pulse. Se l'oggetto wrapper è ancora attivo (i finalizzatori possono occasionalmente falsificare il trigger in alcuni scenari di resurrezione) o il finalizzatore non è stato in grado di spostare il thread del server, l'oggetto finalizzabile dovrebbe registrarsi nuovamente per la finalizzazione.

L'utilizzo dell'oggetto finalizzatore complicherà le cose; se il thread del server può svegliarsi periodicamente senza bisogno di essere spinto, ciò semplificherà la progettazione. Un finalizzatore usato come descritto, tuttavia, dovrebbe essere relativamente robusto anche in scenari che implicano la risurrezione involontaria.