Techniques for exiting / cancelling while loops across threads: bool, ManualResetEvent or CancellationToken
Asked Answered
T

1

9

I am writing a program that has a few threads, each with a while loop that runs until the user specifies it should stop. I thought of a few ways to exit out of the loops, and subsequently the threads, and have outlined these approaches below.

Questions

  1. Are there any pros and cons for each?
  2. Are there cases when you would use one and not another?
  3. I have a heard a few people say they prefer CancellationTokens for exiting threads. What is so appealing with that approach over the other two?

Below follows the approaches.

The bool Approach

Initially, I had declared a bool and just stated that the loops run until the the user sets the bool to false: while(running) { Thread.Sleep(10); /*do work*/ }. I then pondered if that is entirely thread-safe. What if the compiler did some optimization and moved the bool to a register. In that case, the threads would see different values for the bool. As a result, I marked the bool with the volatile keyword to avoid compiler optimizations.

The ManualResetEvent Approach

My next approach was to create a ManualResetEvent, and just say that the bool runs while WaitOne() is false: while(!mre.WaitOne(10)) {/*do work*/}. This will block for 10ms, then run the loop, over and over until we do mre.Set() and the loop exits.

The CancellationToken Approach

This approach I have not actually tried yet, but I read several places that people prefer cancelling threads this way. It is apparently thread-safe. One would define a CancellationTokenSource, call it cts, then pass cts.Token to the method run by the new thread and use an if statement to check if cancel has been requested: while(!token.IsCancellationRequested) { Thread.Sleep(10); /*do work*/ }

UPDATE 1:

I found a similar post that concludes that the MRE approach is significantly slower than the CancellationToken approach. For full info, see here: Stopping a Thread, ManualResetEvent, volatile boolean or cancellationToken

UPDATE 2:

When it comes to comparing the bool approach to the other two, Eric Lippert has a good answer here: AutoResetEvent vs. boolean to stop a thread

UPDATE 3:

I found another relevant piece of information. CancellationTokens cannot be reset once cancelled. So it is not ideal for when you just want to cancel out of a loop temporarily, to start it again later. For that, MRE might be better (as you can Set and Reset, to your heart's content).

Timmerman answered 9/12, 2014 at 23:13 Comment(4)
Cooperative cancellation via CTS is mainly useful if you're calling other methods that also support cancellation, as it allows you to propagate the token down into your call tree.Cunctation
The MRE object can also be passed down the call tree though. And the bool can be a public property, in which case no passing would be needed at all.Timmerman
yes, but since the cooperative cancellation pattern is a standard being pushed by MS in .NET, there are several methods on BCL classes that support CancellationToken as a parameter. One example is Task.Delay (so you can cancel before a delay completes).Cunctation
True, passing CancellatioToken to predefined .NET task methods allows a task to check if it is already cancelled before running, and is as such an example of a place where CancellationToken is more beneficial. Task.Run() is another example there... Unfortunately my threads are long running, so preferred not to use TPL.Timmerman
A
2

Most often your threads don't run in tight loops eating all your CPU cycles, often you are waiting for events of some sort, when you wait you can't really wait on a bool. You could have a timeout on your event, wait for the timeout, check the bool, then go back to waiting. This makes for nasty code and also means your thread won't quit till timeouts occur, or you keep you eat your CPU checking a bool.

Reset Event is ok, you can certainly work with it, But CancelellationToken works nicely and is precisely designed for this.

Alkalimeter answered 10/12, 2014 at 1:32 Comment(1)
The app I am writing requires me to check some conditions that are outside my app and as such there are no events or anything that I can add to ensure it notifies me of change. I have to do some sort of polling. Instead of using the while loops, one could use a Timer, which neatly polls with regular intervals from the thread pool (I have used that approach where appropriate). That is a bit outside the scope of this post though, which is more about comparing cancellation techniques.Timmerman

© 2022 - 2024 — McMap. All rights reserved.