Domanda

Secondo la Documentazione MSDN per ThreadState , lo stato Interrotto può essere inserito in due modi: il thread in uscita o il thread in fase di interruzione.

Esiste un meccanismo per dire se un thread è entrato nello stato Stopped uscendo normalmente? Grazie!

È stato utile?

Soluzione

Un thread può raggiungere lo stato Interrotto in diversi modi:

  • Il metodo principale può uscire senza errori.
  • Un'eccezione non rilevata nel thread può terminarlo.
  • Un altro thread potrebbe chiamare Thread.Abort (), il che provocherà il lancio di un ThreadAbortException su quel thread.

Non so se stai cercando di distinguere tra tutti e tre gli stati, ma se tutto ciò che ti interessa davvero è se il thread è stato completato correttamente, suggerirei di utilizzare una struttura di dati condivisa di qualche tipo (un dizionario sincronizzato lavoro) che il ciclo principale di un thread si aggiorna al termine. È possibile utilizzare la proprietà ThreadName come chiave in questo dizionario condiviso. Altri thread interessati allo stato di terminazione potrebbero leggere da questo dizionario per determinare lo stato finale del thread.

Dopo aver esaminato un po 'di più la Documentazione MSDN , tu dovrebbe essere in grado di differenziare un thread interrotto esternamente utilizzando la proprietà ThreadState . Deve essere impostato su ThreadState.Aborted quando un thread risponde alla chiamata Abort (). Tuttavia, a meno che tu non abbia il controllo del codice thread che viene eseguito, non penso che tu possa distinguere tra un thread che è appena uscito dal suo metodo principale e uno che ha terminato con un'eccezione.

Tieni presente, tuttavia, se controlli il codice che avvia il thread, puoi sempre sostituire il tuo metodo che chiama internamente il codice che esegue la logica del thread principale e inietta il rilevamento di eccezioni (come I descritto sopra) lì.

Altri suggerimenti

Il thread che desideri monitorare il tuo codice? In tal caso, potresti racchiudere il tutto in una classe e generare un evento al termine o utilizzare WaitHandle (vorrei utilizzare ManualResetEvent) per segnalare il completamento. - incapsula completamente la logica di sfondo. Puoi anche utilizzare questo incapsulamento per acquisire un'eccezione e quindi generare un evento.

Qualcosa del genere:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace BackgroundWorker
{
    public class BackgroundWorker
    {
        /// 
        /// Raised when the task completes (you could enhance this event to return state in the event args)
        /// 
        public event EventHandler TaskCompleted;

        /// 
        /// Raised if an unhandled exception is thrown by the background worker
        /// 
        public event EventHandler BackgroundError;

        private ThreadStart BackgroundTask;
        private readonly ManualResetEvent WaitEvent = new ManualResetEvent(false);

        /// 
        /// ThreadStart is the delegate that  you want to run on your background thread.
        /// 
        /// 
        public BackgroundWorker(ThreadStart backgroundTask)
        {
            this.BackgroundTask = backgroundTask;
        }

        private Thread BackgroundThread;

        /// 
        /// Starts the background task
        /// 
        public void Start()
        {
            this.BackgroundThread = new Thread(this.ThreadTask);
            this.BackgroundThread.Start();

        }

        private void ThreadTask()
        {
            // the task that actually runs on the thread
            try
            {
                this.BackgroundTask();
                // completed only fires on successful completion
                this.OnTaskCompleted(); 
            }
            catch (Exception e)
            {
                this.OnError(e);
            }
            finally
            {
                // signal thread exit (unblock the wait method)
                this.WaitEvent.Set();
            }

        }

        private void OnTaskCompleted()
        {
            if (this.TaskCompleted != null)
                this.TaskCompleted(this, EventArgs.Empty);
        }

        private void OnError(Exception e)
        {
            if (this.BackgroundError != null)
                this.BackgroundError(this, new BackgroundWorkerErrorEventArgs(e));
        }

        /// 
        /// Blocks until the task either completes or errrors out
        /// returns false if the wait timed out.
        /// 
        /// Timeout in milliseconds, -1 for infinite
        /// 
        public bool Wait(int timeout)
        {
            return this.WaitEvent.WaitOne(timeout);
        }

    }


    public class BackgroundWorkerErrorEventArgs : System.EventArgs
    {
        public BackgroundWorkerErrorEventArgs(Exception error) { this.Error = error; }
        public Exception Error;
    }

}

Nota: è necessario del codice per evitare che venga richiamato due volte Start e potresti voler aggiungere una proprietà state per passare lo stato al tuo thread.

Ecco un'app console che dimostra l'uso di questa classe:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BackgroundWorker
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Test 1");
            BackgroundWorker worker = new BackgroundWorker(BackgroundWork);
            worker.TaskCompleted += new EventHandler(worker_TaskCompleted);
            worker.BackgroundError += new EventHandler(worker_BackgroundError);
            worker.Start();
            worker.Wait(-1);

            Console.WriteLine("Test 2");
            Console.WriteLine();

            // error case
            worker = new BackgroundWorker(BackgroundWorkWithError);
            worker.TaskCompleted += new EventHandler(worker_TaskCompleted);
            worker.BackgroundError += new EventHandler(worker_BackgroundError);
            worker.Start();
            worker.Wait(-1);

            Console.ReadLine();
        }

        static void worker_BackgroundError(object sender, BackgroundWorkerErrorEventArgs e)
        {
            Console.WriteLine("Exception: " + e.Error.Message);
        }

        private static void BackgroundWorkWithError()
        {
            throw new Exception("Foo");
        }

        static void worker_TaskCompleted(object sender, EventArgs e)
        {
            Console.WriteLine("Completed");
        }

        private static void BackgroundWork()
        {
            Console.WriteLine("Hello!");
        }
    }
}


Potresti voler guardare la classe BackgroundWorker. Ha un gestore di eventi generico per il completamento del thread. Lì puoi verificare se il thread è stato completato a causa di un errore, perché è stato annullato o perché è stato completato correttamente.

Supponendo che il thread principale debba attendere il completamento del thread di lavoro, di solito uso ManualResetEvent. Oppure, per più thread di lavoro, il nuovo CountDownEvent di Parallels Extensions, come so .

Uso un CancelTokenSource per chiedere al thread di uscire con grazia.

Quindi uso var exitedProperly = _thread.Join (TimeSpan.FromSeconds (10); che attende che il thread termini.

Se è uscitoProperly == false , inserisco un errore nel registro.

Uso questo modello principalmente quando mi trovo nella funzione Dispose () e sto cercando di ripulire tutti i thread che ho creato.

Un thread può essere interrotto solo chiamando Thread.Abort (), che si traduce in ThreadAbortException, quindi attraverso la gestione delle eccezioni dovresti essere in grado di determinare un'uscita normale rispetto a un'uscita interrotta.

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