Stopping a Thread, ManualResetEvent, volatile boolean or cancellationToken

dennis_ler picture dennis_ler · Nov 20, 2012 · Viewed 9.1k times · Source

I have a Thread (STAThread) in a Windows Service, which performs a big amount of work. When the windows service is restarted I want to stop this thread gracefully.

I know of a couple of ways

  • A volatile boolean
  • ManualResetEvent
  • CancellationToken

As far as I have found out Thread.Abort is a no go...

What is the best practice ? The work is perfomed in another class than the one where the thread is started, so it is necessary to either introduce a cancellationToken parameter in a constructor or for example have a volatile variable. But I just can't figure out what is smartest.

Update
Just to clarify a little I have wrapped up a very simple example of what I'm talking about. As said earlier, this is being done in a windows service. Right now I'm thinking a volatile boolean that is checked on in the loop or a cancellationToken.... I cannot wait for the loop to finish, as stated below it can take several minutes, making the system administrators of the server believe that something is wrong with the service when they need to restart it.... I can without problems just drop all the work within the loop without problems, however I cannot do this with a Thread.Abort it is "evil" and furthermore a COM interface is called, so a small clean up is needed.

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

UPDATE
Just examined performance, using a cancellationToken where the isCancelled state is "examined" in the code, is much faster than using a waitOne on a ManualResetEventSlim. Some quick figuers, an if on the cancellationToken iterating 100.000.000 times in a for loop costs me approx. 500 ms, where the WaitOne costs approx. 3 seconds. So performance in this scenario it is faster to use the cancellationToken.

Answer

Lloyd picture Lloyd · Nov 20, 2012

I tend to use a bool flag, a lock object and a Terminate() method, such as:

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

Asides from Terminate() all the other fields and methods are private to the "worker" class.