Как определить, успешно ли завершился поток? - PullRequest
3 голосов
/ 16 июня 2009

Согласно документации MSDN для ThreadState , состояние «Остановлено» может быть введено одним из двух способов: выход из потока или прерывание потока.

Существует ли какой-либо механизм для определения того, вошел ли поток в состояние «Остановлено» при нормальном выходе? Спасибо!

Ответы [ 6 ]

3 голосов
/ 16 июня 2009

Является ли поток, который вы хотите контролировать свой код? Если это так, вы можете обернуть все это в классе и либо вызвать событие, когда закончите, либо использовать WaitHandle (я бы использовал ManualResetEvent), чтобы сообщить о завершении. - полностью инкапсулировать фоновую логику. Эту инкапсуляцию также можно использовать для захвата исключения, а затем для вызова события.

Примерно так:


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

}

Примечание: вам нужен код, чтобы предотвратить повторный вызов Start, и вы можете добавить свойство состояния для передачи состояния в ваш поток.

Вот консольное приложение, демонстрирующее использование этого класса:


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!");
        }
    }
}


3 голосов
/ 16 июня 2009

Поток может достичь состояния Stopped несколькими способами:

  • Это основной метод может выйти без ошибок.
  • Неопределенное исключение в потоке может прервать его.
  • Другой поток может вызвать Thread.Abort (), что вызовет исключение ThreadAbortException в этом потоке.

Я не знаю, хотите ли вы провести различие между всеми тремя состояниями, но если все, что вас действительно интересует, это то, успешно ли завершен поток, я бы предложил использовать какую-либо общую структуру данных (синхронизированный словарь работа), что основной цикл потока обновляется по мере его завершения. Вы можете использовать свойство ThreadName в качестве ключа в этом общем словаре. Другие потоки, которые заинтересованы в состоянии завершения, могут читать из этого словаря, чтобы определить окончательное состояние потока.

Изучив документацию MSDN , вы сможете различить внешне прерванный поток, используя свойство ThreadState. Это должно быть установлено на ThreadState.Aborted, когда поток отвечает на вызов Abort (). Однако, если у вас нет контроля над выполняемым кодом потока, я не думаю, что вы сможете различить поток, который только что вышел из основного метода, и поток, который завершился с исключением.

Имейте в виду, однако, что если вы управляете кодом, который запускает поток, вы всегда можете заменить свой собственный метод, который внутренне вызывает код, который выполняет логику основного потока и обнаруживает исключения исключений (как я описал) выше) там.

1 голос
/ 16 июня 2009

Предполагая, что основной поток должен дождаться успешного завершения рабочего потока, я обычно использую ManualResetEvent. Или для нескольких рабочих потоков - новое CountDownEvent от Parallels Extensions, например , так .

1 голос
/ 16 июня 2009

Возможно, вы захотите взглянуть на класс BackgroundWorker. У него есть общий обработчик событий для завершения потока. Там вы можете проверить, завершен ли поток из-за ошибки, потому что он был отменен или потому что он успешно завершился.

0 голосов
/ 17 декабря 2015

Я использую CancellationTokenSource, чтобы попросить нить выйти изящно.

Затем я использую var exitedProperly = _thread.Join(TimeSpan.FromSeconds(10);, ожидающий завершения потока.

Если exitedProperly==false, я помещаю ошибку в журнал.

Я использую этот шаблон в основном, когда я нахожусь в функции Dispose(), и я пытаюсь очистить все созданные мной потоки.

0 голосов
/ 16 июня 2009

Поток может быть прерван только путем вызова Thread.Abort (), что приводит к исключению ThreadAbortException, поэтому с помощью обработки исключений вы сможете определить нормальный выход против прерванного выхода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...