2010-02-10 2 views
1

Я общая резьба n00b, и я хочу выяснить способ сначала закрыть нить, спросив красиво, затем силой.Как остановить поток от завершения в определенном разделе кода?

У меня есть ProcessManager класс, который запускает кучу Process класса нитей:

public class ProcessManager 
{ 
    private IProcessRepository _processRepository; 
    private List<Thread> _threads = new List<Thread>(); 

    public ProcessManager(IProcessRepository processRepository) 
    { 
     _processRepository = processRepository; 
    } 

    public void StartAllProcesses() 
    { 
     foreach (var process in _processRepository.GetActiveProcesses()) 
     { 
      var thread = new Thread(process.ProcessUntilStopped); 
      _threads.Add(thread); 
      thread.Start(); 
     } 
    } 

    public void StopAllProcesses() 
    { 
     // What to do here? 
    } 
} 

public class Process 
{ 
    private bool _stopFlag = false; 

    public void ProcessUntilStoppped() 
    { 
     while (true) 
     { 
      if (_stopFlag) return; 

      // I don't want this call interrupted by Thread.Join(), 
      // but it's important for multiple Processes to be able to DoWork() 
      // simultaneously. 
      DoWork(); 
     } 
    } 

    public void Stop() 
    { 
     _stopFlag = true; 
    } 
} 

Как я могу получить мою нить не прерывается во время вызова DoWork() и вместо того, чтобы ждать до следующего цикла итерации, чтобы остановить? (Я понимаю, что lock здесь не подходит, потому что несколько потоков должны иметь возможность звонить DoWork(), пока они работают, поэтому нет ресурса, который должен быть взаимоисключающим.)

Как проверить, мой поток прекратился красиво, а если нет, заставьте его остановиться? (Я знаю, что Thread.Abort() - плохая карма.)

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

+0

Мой ответ был опубликован после того, как вы приняли ответ, и это нормально.Просто убедитесь, что вы отмечаете _stopFlag как изменчивый. –

ответ

1

StopAllProcesses должен проходить через каждый поток в коллекции и вызывать .Stop(). Тогда, что вы в идеале хотите, это способ сказать из каждого потока, что он остановился (флаг, который вы установили при завершении ProcessUntilStoppped?)

Это должно со временем работать, но это может занять некоторое время, в зависимости от того, как долго DoWork() принимает. Возможно, вам придется поместить таймер в том, что говорит, если все потоки не закончили после (60 секунд?), Чтобы прервать потоки.

1

Прежде всего, убедитесь, что поле _stopFlag отмечено как volatile. Насколько я понимаю ваш код, флаг будет установлен основным потоком, но будет прочитан потоком процесса. Обычно это связано с синхронизацией с использованием инструкции lock. Однако для определенных типов, включая bool, вы можете просто отметить флаг как изменчивый и избежать блокировки.

DoWork() не будет «прерван» Thread.Join(). Thread.Join() блокирует строку , вызывающую, до тех пор, пока конечный поток не завершится. Поэтому, если ваш основной поток должен был вызвать Thread.Join() и метод DoWork() занял 30 минут, основной поток будет заблокирован на 30 минут. Очевидно, это не то, что вы хотите.

Остановка ниток проста. StopAllProcesses() должен перебирать каждый из потоков в коллекции и вызывать их соответствующие методы Stop(). Вопрос заключается в том, как избежать возврата основного потока из StopAllProcesses() до тех пор, пока все потоки не будут остановлены.

Один из способов будут вызывать в заданное время версии Thread.Join(Int32) для каждого потока, как это:

public void StopAllProcesses() 
{ 
    // Stop the threads. 
    _threads.ForEach(thread => thread.Stop()); 
    // Wait for the threads to terminate. 
    _threads.ForEach(thread => { 
     try { 
      if (!thread.Join(1000)) // 1000 milliseconds = 1 second 
      { 
       thread.Abort(); // try to avoid using this! 
      } 
     } catch { 
      // do something appropriate here 
     } 
    }); 
} 

Другим способом связать объект Thread.ManualResetEvent с каждым потоком. Объект будет сигнализироваться каждым потоком, когда он выйдет из функции ProcessUntilStopped(). Метод StopAllProcesses() будет выглядеть следующим образом:

public void StopAllProcesses() 
{ 
    // Create the list of wait handles where Terminated is a public property 
    // of the Process thread class whose type is ManualResetEvent. 
    List<WaitHandle> handles = new List<WaitHandle>(); 
    _threads.ForEach(thread => handles.Add(thread.Terminated)); 
    // Stop the threads. 
    _threads.ForEach(thread => thread.Stop()); 
    // Wait for the threads to terminate. 
    try { 
     if (!WaitHandle.WaitAll(handles.ToArray(), 1000)) 
     { 
      _threads.ForEach(thread => thread.Abort()); 
     } 
    } catch { 
     // do something appropriate here 
    } 
} 

Если этот последний пример используется, просто убедитесь, что Terminated.Set() вызывается до процесса нить выходит из функции ProcessUntilStopped().

Смежные вопросы