Stream Stop, ManualResetEvent, volatile boolean or cancelationToken

I have Thread (STAThread) in a windows service that does a lot of work. When the Windows service is restarted, I want to gracefully stop this thread.

I know a couple of ways

  • Volatile logic
  • ManualResetEvent
  • CancellationToken

As far as I understand, Thread.Abort is not a race ...

What is the best practice? The work is done in a different class than where the thread starts, so you must either enter the cancelationToken parameter in the constructor, or, for example, have a mutable variable. But I just can’t understand what is the smartest.

Update
To clarify a bit, I wrapped a very simple example of what I'm saying. As mentioned earlier, this is done in the Windows service. Right now I’m thinking of a volatile boolean that is being checked in a loop or cancelationToken .... I can’t wait for the loop to finish, as described below, it can take several minutes, as a result of which the server system administrators think that something is wrong with service, when you need to restart it ... I can easily drop all the work in the loop without problems, however I can not do it with Thread.Abort, this is "evil", and in addition, the COM interface is called, therefore minor cleaning required.

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 review the performance using cancelationToken, where the isCancelled state is “checked” in the code, much faster than using waitOne on ManualResetEventSlim. Some quick figures, if when canceling a Token, iterating 100,000,000 times in a loop, it costs me approx. 500 ms, where WaitOne is approx. 3 seconds Thus, the performance in this scenario is faster than cancelationToken.

+2
source share
4 answers

You have not published enough of your implementation, but I would highly recommend the CancellationToken , if available to you. It is simple enough to use and understand in terms of maintainability. You can also set up co-revocation if you decide to have more than one workflow.

If you find yourself in a situation where this thread may be blocked for long periods of time, it is best to configure your architecture so that this does not happen. You should not start topics that will not play well when you order them to stop. If they don’t stop when you ask them, the only real way is to tear down the process and let the OS kill them.

Eric Lippert wrote a fantastic answer to a somewhat related question here .

+3
source

I tend to use the bool flag, the lock object, and the Terminate () method, for example:

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

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

+4
source

Use WaitHandle, most preferably ManualResetEvent. It’s best to allow everything in your loop. This is the safest way to achieve your goal.

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

Then, to use it:

 Thread thread = new Thread(() => { thing.DoProcessing(); }); thread.Start(); //Some time later... thing.Terminate(); 

If you have a particularly lengthy process in your DoSomething implementation, you can invoke this asynchronously and provide it with status information. It can get quite complicated, though - it's better to just wait until your process is complete and then exit if you can.

+3
source

There are two situations in which you can find your stream:

  • Treatment.
  • Blocking

In the event that your thread is processing something, you must wait until the processing of your thread is complete so that it can exit safely. If this is part of the duty cycle, then you can use the boolean flag to end the cycle.

In case of blocking the thread, you need to wake the thread and process it again. A thread can block on a ManualResetEvent , a database call, a socket call, or whatever you might block. To wake it, you must call the Thread.Interrupt() method, which will raise a ThreadInterruptedException .

It might look something like this:

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

And here is how you can use it:

 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 in a blocking state t.Interrupt(); // Wait for the thread to exit t.Join(); 
+2
source

Source: https://habr.com/ru/post/979372/


All Articles