Как я безопасно останавливаю поток.NET C#, работающий в службе Windows?

Начнитесь простые, и проложите себе путь оттуда. Так как это - Ваш первый опыт, работающий с этим, используйте документ слова с пунктами маркированного списка. Запишите его, перечитайте его и обеспечьте достаточно детали, что это имеет смысл. Для технических спецификаций можно хотеть привести разработчика к решению, но для функциональных спецификаций, "как" должно быть абсолютно недостающим.

13
задан Peter Mortensen 19 November 2009 в 22:12
поделиться

7 ответов

Когда у меня есть что-то подобное, я обычно использую ManualResetEvent . Это устанавливается в вызове Stop () . Затем я жду с таймаутом:

for (;;)
{
    if (_stop.WaitOne(timeout))
        break;
    DoSomething();
}
21
ответ дан 1 December 2019 в 21:12
поделиться

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

Вместо Thread Вы можете, например, засыпать 500 мс и проверить, установлен ли флаг прерывания по-прежнему, спать еще 500 мс и т. Д. До истечения 30 минут, затем выполнить работу и т. Д. (Это был бы прагматичный подход). Если вам нужно что-то более элегантное, вы можете использовать ManualResetEvent с таймаутом, чтобы дождаться, пока основной поток сигнализирует, что пора прервать работу.

2
ответ дан 1 December 2019 в 21:12
поделиться

Вот один из способов сделать это. Добавьте в свой класс следующие переменные:

private readonly object syncObject = new object();
private bool stopping;
private bool stopped = true;

Затем в OnStart вы делаете что-то вроде этого (у меня есть вспомогательный метод, который выполняет некоторые записи в этом примере, а метод «Run» выполняет фактическую работу).:

    public override void OnStart()
    {
        while (stopping)
        {
            Thread.Sleep(MSECS_SLEEP_FOR_STOP);
        }

        lock (syncObject)
        {
            // make sure task isn't already started
            if (!stopped)
            {
                Helper.WriteToLog(logger, Level.INFO,
                    string.Format("{0} {1}", TASK_NAME, "is already started."));
                return;
            }
            stopped = false;
        }

        // start task in new thread
        Thread thread = new Thread(Run);
        thread.Start();

        Helper.WriteToLog(logger, Level.INFO,
            string.Format("{0} {1}", TASK_NAME, "was started."));
    }

Ваш метод "Run", который выполняет работу потока, будет выглядеть следующим образом (processInterval - это время ожидания между запусками, вы можете установить его в конструкторе или просто жестко запрограммировать):

    private void Run()
    {
        try
        {
            while (!stopping)
            {
                // do work here

                // wait for process interval
                DateTime waitStart = DateTime.Now;
                while (((DateTime.Now - waitStart).TotalMilliseconds < processInterval) && !stopping)
                {
                    // give processing time to other threads
                    Thread.Sleep(MSECS_SLEEP_FOR_CHECK);
                }
            }
            lock (syncObject)
            {
                stopped = true;
                stopping = false;
            }

            Helper.WriteToLog(logger, Level.INFO,
                string.Format("{0} {1}", TASK_NAME, "was stopped."));
        }
        catch (Exception e)
        {
            // log the exception, but ignore it (i.e. don't throw it)
            Helper.LogException(logger, MethodBase.GetCurrentMethod(), e);
        }
    }

Затем в OnStop , вы должны сделать это:

    public override void OnStop()
    {
        lock (syncObject)
        {
            if (stopping || stopped)
            {
                Helper.WriteToLog(logger, Level.INFO,
                    string.Format("{0} {1}", TASK_NAME, "is already stopped."));
                return;
            }
            stopping = true;
        }
    }
1
ответ дан 1 December 2019 в 21:12
поделиться

Если вы хотите найти слово, которое вы можете использовать

var word = words.Where(item => item.IsKey).First();

Это дает вам первый элемент, для которого IsKey истинно (если это может быть не вы может захотеть использовать .

1
ответ дан 1 December 2019 в 21:12
поделиться

Попробуйте использовать флаг автосброса, чтобы обработать остановку службы. В этом случае вам не нужно будет выполнять прерывание потока. Добавили пример кода ниже

namespace WorkService
{
    public partial class WorkService : ServiceBase
    {
    AutoResetEvent serviceStopEvent = new AutoResetEvent( false);

        public WorkService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            Thread workerThread = new Thread(new ThreadStart(DoWork));
            workerThread.Start();
        }

        protected override void OnStop()
        {
           serviceStopEvent.Set();
        }

        static void DoWork()
        {
            int sleepMinutes = 30;
        WaitHandle[ ] handles = new WaitHandle[ ] { serviceStopEvent };

            while (WaitHandle.WaitAny( handles))
            {
                 ActualWorkDoneHere();

            }
        }

    }
}

Ура, Бхарата.

0
ответ дан 1 December 2019 в 21:12
поделиться

Ух ты, все делают это так сложно. Используйте таймер:

На гонках: В исходном посте была исправлена ​​гонка в OnStop. Насколько мне известно, перевод службы в остановленное состояние не приведет к прерыванию потоков пула потоков, которые используются для обслуживания таймера. Состояние срабатывания таймера и одновременной остановки службы не имеет значения. ActualWorkDoneHere () либо запустится, либо не запустится. Оба являются приемлемыми условиями.

namespace WorkService
{
    public partial class WorkService : ServiceBase
    {
        protected const int sleepMinutes = 30;
        protected System.Timers.Timer _interval;
        protected bool _running = false;

        public WorkService()
        {
            InitializeComponent();
            _interval = new System.Timers.Timer();
            _interval.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            _interval.Interval = sleepMinutes * 60 * 1000;
            _running = false;
        }

        protected override void OnStart(string[] args)
        {
            _running = true;
            _interval.Enabled = true;
        }

        protected override void OnStop()
        {
            _interval.Enabled = false;
            _running = false;
        }

        private static void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            if(_running)
                ActualWorkDoneHere();
        }
    }
}
2
ответ дан 1 December 2019 в 21:12
поделиться

Моя служба прослушивает сетевой сокет, поэтому я создал пару объединенных сетевых сокетов и использовал системный вызов select для прослушивания обоих. Если присоединенная пара сообщила о готовности к чтению, я знал, что нужно завершить работу службы.

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

0
ответ дан 1 December 2019 в 21:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: