Остановка потока, ManualResetEvent, логическое значение volatile или cancellationToken

У меня есть поток (STAThread) в службе Windows, который выполняет большой объем работы. Когда служба Windows перезапускается, я хочу изящно остановить этот поток.

Я знаю несколько способов

Летучий логическийManualResetEventCancellationToken

Насколько я выяснил Thread.Abort нет дела ...

Какова лучшая практика? Работа выполняется в другом классе, чем тот, в котором запущен поток, поэтому необходимо либо ввести параметр cancellationToken в конструктор, либо, например, иметь переменную volatile. Но я просто могуне понять, что умнее.

Обновить

Просто чтобы прояснить немного, я обернул очень простой пример того, что яЯ говорю о. Как было сказано ранее, это делается в службе Windows. Прямо сейчас яя думаю о энергозависимом логическом значении, которое проверено в цикле или cancellationToken .... Я не могу дождаться окончания цикла, как указано ниже, это может занять несколько минут, из-за чего системные администраторы сервера считают, что что-то не так с сервис, когда им нужно перезапустить его .... Я могу без проблем просто отбросить всю работу в цикле без проблем, однако я не могу сделать это с потоком. Аборт это "злой" и, кроме того, вызывается интерфейс COM, поэтому требуется небольшая очистка.

Class Scheduler{
  private Thread apartmentThread;
  private Worker worker;

  void Scheduling(){
    worker = new Worker();
    apartmentThread = new Thread(Run);
    apartmentThread.SetApartmentState(ApartmentState.STA);
    apartmentThread.Start();    
  }

  private void Run() {
    while (!token.IsCancellationRequested) {
      Thread.Sleep(pollInterval * MillisecondsToSeconds);
      if (!token.IsCancellationRequested) {
        worker.DoWork();
      }
    }
  }
}

Class Worker{
  //This will take several minutes....
  public void DoWork(){
    for(int i = 0; i < 50000; i++){
      //Do some work including communication with a COM interface
      //Communication with COM interface doesn't take long
    }
  }
}

ОБНОВИТЬ

Только что проверил производительность, используя cancellationToken, где состояние isCancelled равно "рассмотрел» в коде, намного быстрее, чем использование waitOne для ManualResetEventSlim. Несколько быстрых вычислений, если итерация на cancellationToken 100.000.000 раз в цикле for обходится мне в ок. 500 мс, где WaitOne стоит ок. 3 секунды Так что производительность в этом сценарии быстрее использовать cancellationToken.

 Maxim Zabolotskikh20 нояб. 2012 г., 16:38
взгляните на связанный вопрос о событии сброса и энергозависимостиstackoverflow.com/questions/11953234/... Лично я обычно использую ManualResetEvent для вашего случая, для этогоЧто-то, предоставленное системой специально для такого рода задач.

Ответы на вопрос(4)

объект блокировки и метод Terminate (), например:

object locker = new object();
bool do_term = false;

Thread thread = new Thread(ThreadStart(ThreadProc));
thread.Start();

void ThreadProc()
{
    while (true) {
        lock (locker) {
            if (do_term) break;
        }

        ... do work...
    }
}

void Terminate()
{
    lock (locker) {
        do_term = true;
    }
}

Помимо Terminate () все остальные поля и методы являются частными для "работник» class.I»

 Lloyd16 июл. 2015 г., 15:22
@ witoong623 Потому что два разных потока обращаются к одной и той же переменной.
 Matt Davis20 нояб. 2012 г., 17:13
буду предлагать маркировкуbool как .volatile
 witoong62316 июл. 2015 г., 02:38
Мне просто интересно, почему вы используете оператор блокировки? это потому, что ваш пример является распространенным способом остановки, когда множество потоков выполняется в одном и том же коде?
 Lloyd16 июл. 2015 г., 17:09
Два потока могут выполняться одновременно, это означает, что состояние переменной может изменяться между чтениями и записью между потоками.
 witoong62316 июл. 2015 г., 16:13
@ Ллойд, я нене понимаю ... яЯ новичок в потоках, что ямы поняли, что разные потоки выполняются в одном и том же коде, но не в одном и том же контексте (не уверен, что такое контекст? что-то вроде другого экземпляра), поэтому он не 'не нужно заявление о блокировке. Я неправильно понимаю?
 Lloyd20 нояб. 2012 г., 17:20
Тот'Правда, это исключило бы необходимость блокировки.

Обработка.Блокирование.

В случае, когда ваш поток что-то обрабатывает, вы должны подождать, пока ваш поток завершит обработку, чтобы он мог безопасно выйти. Если оно's часть рабочего цикла, то вы можете использовать логический флаг для завершения цикла.

В случае, когда ваш поток блокируется, вам нужно разбудить ваш поток и снова обработать его. Поток может блокироватьManualResetEventданные, базовый вызов, вызов сокета или что-то еще, что вы могли бы заблокировать. Чтобы разбудить его, вы должны позвонитьThread.Interrupt() метод, который поднимет.ThreadInterruptedException

Это может выглядеть примерно так:

private object sync = new object():
private bool running = false;

private void Run()
{
    running = true;
    while(true)
    {
        try
        {
            lock(sync)
            {
                if(!running)
                {
                    break;
                }
            }

            BlockingFunction();
        }
        catch(ThreadInterruptedException)
        {
            break;
        }
    }
}

public void Stop()
{
    lock(sync)
    {
        running = false;
    }
}

И вот как вы можете использовать это:

MyRunner r = new MyRunner();
Thread t = new Thread(()=>
{
    r.Run();
});

t.IsBackground = true;
t.Start();

// To stop the thread
r.Stop();

// Interrupt the thread if it's in a blocking state
t.Interrupt();

// Wait for the thread to exit
t.Join();

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

ManualResetEvent _stopSignal = new ManualResetEvent(false); // Your "stopper"
ManualResetEvent _exitedSignal = new ManualResetEvent(false);

void DoProcessing() {
    try {
        while (!_stopSignal.WaitOne(0)) {
            DoSomething();
        }
    }
    finally {
        _exitedSignal.Set();
    }
}

void DoSomething() {
    //Some work goes here
}

public void Terminate() {
    _stopSignal.Set();
    _exitedSignal.WaitOne();
}

Тогда использовать это:

Thread thread = new Thread(() => { thing.DoProcessing(); });
thread.Start();

//Some time later...
thing.Terminate();

Если у вас есть особенно длительный процесс в вашем "Сделай что-нибудь" реализации, вы можете вызвать это асинхронно и предоставить ему информацию о состоянии. Это может оказаться довольно сложным - лучше просто подождать, пока ваш процесс не закончится, а затем выйти, если вы можете.

 dennis_ler21 нояб. 2012 г., 10:06
Это был бы хороший способ сделать это, однако неМожет ли быть проблема, что мой поток, выполняющий DoProcessing, является STAThread?
 Sean H21 нояб. 2012 г., 16:06
Это не должноэто не проблема. (по крайней мере, это не былот когда я его запустил)
Решение Вопроса

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

Если вы оказались в ситуации, когда этот поток может блокироваться в течение длительного времени, он 'Лучше всего настроить вашу архитектуру так, чтобы это нет происходит. Ты не долженне начинать темы, которые выиграютиграть хорошо, когда вы говорите им остановиться. Если они неОстановитесь, когда вы спросите их, единственный реальный способ - это остановить процесс и позволить ОС убить их.

Эрик Липперт опубликовал фантастический ответ на несколько связанный с этим вопросВот.

 dennis_ler21 нояб. 2012 г., 15:47
Как упоминалось выше в моем последнем обновлении, я обнаружил, что лучше всего использовать CancellationToken ...

Ваш ответ на вопрос