Domanda

Ho una libreria di classi C #, che avvia un thread in background dei consumatori (pigramente), che ascolto per le attività per completare da una coda produttore / consumatore. Questa libreria di classi può essere utilizzato da qualsiasi tipo di applicazione .NET ed è attualmente in uso in un sito Web ASP.NET MVC. Il filo consumatore è bloccato la maggior parte del tempo fino arriva una richiesta nella coda. Ci dovrebbe essere un solo thread consumatore per un determinato dominio app.

voglio essere in grado di spegnere correttamente il filo consumatore alla chiusura dell'applicazione, indipendentemente da quale tipo di applicazione è, ad esempio Windows Form, un'applicazione console, o WPF. Il codice applicazione stessa deve ignorare il fatto che questa discussione è persistente in attesa di essere terminato.

Come posso risolvere questo problema vita discussione dal punto di vista del libreria di classi? C'è qualche evento di arresto applicazione globale posso legare in per abbattere il filo con grazia, allora?

In questo momento, dal momento che è in un dominio applicazione ASP.NET MVC, in realtà non importa quanto tali non vengono mai abbattute con grazia in ogni caso. Ma ora che sto iniziando a utilizzare in attività di applicazione console in programma progettato per terminare, voglio risolvere questo problema una volta per tutte. L'applicazione di console non termina poiché il filo consumatore è ancora attiva e bloccata in attesa di richieste. Ho esposto una proprietà Discussione public static sulla classe di emettere una chiamata Abort() quando la console app sta uscendo ma questo è, francamente, disgustoso.

Tutti gli indicatori sarebbe apprezzato! Ancora una volta, non voglio dover scrivere Windows Form o WPF o codice specifico un'applicazione console per risolvere il problema. Una soluzione generica bello che ogni consumatore della libreria di classi possibile utilizzare sarebbe meglio.

È stato utile?

Soluzione

Impostare la proprietà IsBackground del thread per true. Allora non impedirà il processo dalla fine.

http://msdn.microsoft.com /en-us/library/system.threading.thread.isbackground.aspx

In alternativa, invece di utilizzare Abort, uso Interrupt. Molto meno disgustoso.

Altri suggerimenti

Questo è un po 'complicato. Si trova proprio sulla pista che si vuole evitare un'interruzione come che potrebbero interrompere il filo nel bel mezzo di un'operazione importante (la scrittura su un file, ecc). L'impostazione della proprietà IsBackground avrebbe lo stesso effetto.

Quello che dovete fare è rendere il vostro blocco di coda annullabile. Peccato che non si sta utilizzando .NET 4.0 ancora altrimenti avrebbe potuto utilizzare la classe BlockingCollection. Nessun problema però. Sarà solo necessario modificare la coda personalizzato in modo che i sondaggi periodicamente per un segnale di arresto. Ecco un'implementazione rapida e sporca che ho appena montata su usando l'implementazione canonica di una coda di blocco come punto di partenza.

public class CancellableBlockingCollection<T>
{
    private Queue<T> m_Queue = new Queue<T>();

    public T Take(WaitHandle cancel)
    {
        lock (m_Queue)
        {
            while (m_Queue.Count <= 0)
            {
                if (!Monitor.Wait(m_Queue, 1000))
                {
                    if (cancel.WaitOne(0))
                    {
                        throw new ThreadInterruptedException();
                    }
                }
            }
            return m_Queue.Dequeue();
        }
    }

    public void Add(T data)
    {
        lock (m_Queue)
        {
            m_Queue.Enqueue(data);
            Monitor.Pulse(m_Queue);
        }
    }
}

Si noti come il metodo Take accetta un WaitHandle che può essere testato per il segnale di annullamento. Questo potrebbe essere lo stesso handle di attesa utilizzato in altre parti del codice. L'idea qui è che la maniglia di attesa viene interrogato su un certo Take all'interno dell'intervallo e se viene segnalata quindi l'intero metodo getta. Il presupposto è che gettare Take interno è a posto perché è in un punto sicuro perché il consumatore non avrebbe potuto essere nel bel mezzo di un'operazione importante. Si può solo permettere il filo per disintegrare su l'eccezione a questo punto. Ecco ciò che il thread consumatore può sembrare.

ManualResetEvent m_Cancel = new ManualResetEvent(false);
CancellableBlockingCollection<object> m_Queue = new CancellableBlockingCollection<object>();

private void ConsumerThread()
{
  while (true)
  {
    object item = m_Queue.Take(m_Cancel); // This will throw if the event is signalled.
  }
}

Ci sono vari altri modi si potrebbe annullare il metodo Take. Ho scelto di utilizzare un WaitHandle, ma potrebbe facilmente essere fatto utilizzando un semplice bandiera bool per esempio.

Aggiornamento:

Come Steven ha sottolineato nella commenti Thread.Interrupt essenzialmente fa questo già. Ciò causerà un filo di un'eccezione in un blocco delle chiamate ... più o meno quello che cercavo in questo esempio, ma con molto più codice. Un avvertimento con Thread.Interrupt è che funziona solo durante le chiamate di blocco in scatola in .NET BCL (come WaitOne, etc.). Quindi non si sarebbe in grado di annullare un calcolo lungo in esecuzione in corso come si farebbe se si è utilizzato un approccio più manuale come quello di questa risposta. E 'sicuramente ottimo strumento per tenere in tasca posteriore e potrebbe anche essere utile nel vostro scenario specifico.

Se si utilizza .NET 4.0 un CancelationToken può essere utilizzato per segnalare il filo che è il momento di spegnere correttamente. E 'molto utile in quanto la maggior parte dei comandi di attesa permettono di passare un token e se il thread è in attesa quando il filo viene annullata verrà automaticamente fuori l'attesa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top