9

I have a worker std::thread and I want its main loop to check if some other thread tells it to stop looping and exit.
What is a good cross platform way to do this? Does boost provide some event object for it?
Is using just a bool considered thread-safe?

shoosh
  • 76,898
  • 55
  • 205
  • 325
  • What do you mean by “using” here? Generally, the answer is always “no”: no type except for `atomic` is inherently thread-safe. – Konrad Rudolph Jun 02 '11 at 12:13
  • Locks (`std::mutex`) are not appropriate for this. Unfortunately, C++0x doesn't inherit `boost::thread::interrupt`, which is a great shame. See [these](http://stackoverflow.com/questions/2845704/how-to-interrupt-a-waiting-c0x-thread) [two](http://stackoverflow.com/questions/2790346/c0x-thread-interruption) questions. – Lightness Races in Orbit Jun 02 '11 at 12:27
  • 1
    @Tomalak: Please explain the problem with using a mutex. It seems the easiest and most obvious solution. – Nicholas Knight Jun 02 '11 at 12:31
  • @Nicholas: Locks lock things. They are not signals. It is inappropriate. – Lightness Races in Orbit Jun 02 '11 at 12:32
  • 3
    @Tomalak: Sorry, but that's hand-wavy academic nonsense, not a technical argument. The behaviour of a mutex is well-understood and not going to change, and it solves the problem very nicely. The history of programming and, indeed, the world, is replete with new and unexpected uses of old tools. I'd also argue that mutexes inherently _are_ signals -- normally a signal to a thread _not_ to do something. – Nicholas Knight Jun 02 '11 at 12:38
  • 1
    @Nicholas: \*shrug\* I'm not an academic, and I'm pretty happy with the quality of code that results from my technical arguments during spec meetings. You're of course welcome to disagree. – Lightness Races in Orbit Jun 02 '11 at 12:57
  • 1
    A bool isn't guaranteed to be thread safe. Assuming you have c++11 you probably want to look at std::atomic_flag which is designed ot be thread safe for exactly this kind of cross thread flag. – jcoder Apr 25 '13 at 07:31

4 Answers4

6

.. well it depends. What is the thread doing? Does it block on anything, I/O, sleep, or some other API?

If it's just CPU-looping all the time and it does not matter about exactly when it stops and it stops and exits, then just use a boolean. There's no point, in this case, in locking up a 'stopAndExit' boolean.

If the work thread does not read it as true on one loop when perhaps it should, because of some lack of atomicity, it will get it next time round. Why perform locks/API calls when you don't have to? A kernel level call from user to acquire/release a synchronisation object will take ages compared with a simple 'if (stop) exit;', wasting time on each loop that could have been used for calculation, or whatever you do in your CPU-bound thread.

It is cross-platform

Regards, Martin

sehe
  • 374,641
  • 47
  • 450
  • 633
Martin James
  • 24,453
  • 3
  • 36
  • 60
  • 3
    +1 For making people think if expensive synchronization methods are really neccessary all the time. – Christian Rau Jun 02 '11 at 13:30
  • 9
    I don't see how this could be guaranteed to work. Isn't it a valid optimization for a compiler to cache the value of a variable in a register if there is no memory barrier or volatile read semantics? In that case, it will never see the update. I can tell you that this type of problem **definitely** exists in .NET (it's possible for a multi-threaded program to get into an infinite loop if it constantly checks a bool condition, even if it the value is actually now `true` because it was updated from another thread). – bobbymcr Dec 25 '11 at 20:28
  • @bobbymcr - try it with anything except a grossly trivial thread, designed specifically to show up this issue. It won't happen on Intel, I won't happen on Spark, it won't happen on anything I've ever used. It won't happen because the registers get spilled out into L1 cache on a driver interrupt, on a function call, if you breathe too loudly. – Martin James Apr 07 '13 at 22:33
  • 1
    The problem is not so much a missing hardware synchronization, but the fact that the compiler is allowed to replace `while(!exit){...}` with `while(true){...}` as long as there aren't any opaque function calls or synchronization primitives in the loop body and then it doesn't matter anymore, what the hw does or doesn't. Although I admit, that there are probably not too many usefull loop bodies with that properties that you might want to abort. Anyway, the solution is to use a `std::atomic` and with relaxed memory ordering there isn't even an overhead. – MikeMB Jan 27 '16 at 23:49
2

What you could have is a std::mutex that is shared between primary and your worker thread.

Your worker thread can acquire std::mutex::try_lock before commencing any work and release it intermittently . The main thread can lock this mutex when it wants your worker thread to shut down. Your worker thread can detect this using try_lock and exit

parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85
  • 4
    Possible performance benefit to reversing it? main thread holds on to the lock, worker thread tries (non-blockingly) to acquire it at the start of the loop, exits on success. Saves cycles from releasing every time, and main thread doesn't have to sit around waiting for a loop to finish if it's got other stuff to do. – Nicholas Knight Jun 02 '11 at 12:29
0

std::mutex and it's relatives are the core mechanisms. std::lock_guard and unique_lock build on this, and std::condition_variable are good for this purpose.

holtavolt
  • 4,378
  • 1
  • 26
  • 40
0

As you mentioned boost, there is boost::thread you can use.

In your case, after starting a thread, you could interrupt it by using the interrupt() method. The receiver thread can check for interrupt requests with the boost::this_thread::interruption_point() method. This will cause the thread to exit if there is an interruption request, otherwise, it will ignore it.

You can check the documentation here: Boost Thread Documentation

Daniel
  • 127
  • 1
  • 8
  • Note that std::thread is based on the Boost threads design [link]http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2184.html - so arguably the std will be more portable (although currently there's probably little difference in supported platforms.) – holtavolt Jun 02 '11 at 13:03
  • @Howard Do you happen to know why it was removed, or maybe even more appalling: why C++14/17 still don't have it? – rubenvb Jun 03 '17 at 23:10
  • Yes. I was chair of the LWG when this happened (prior to C++11). There was an unofficial POSIX presence on the committee that convinced enough members on the committee that cancellation could not be implemented, despite the fact that working implementations existed. There was too much confusion between "kill a thread" and "cooperatively ask a thread to end". The `interrupt` syntax was born from that confusion, but was still not enough to sway a consensus in favor of it. It has not been revisited because `std::thread` is now set in stone. – Howard Hinnant Jun 04 '17 at 03:24
  • That being said, one can now portably build a cancelable thread on top of `std::thread`. But no one has proposed to standardized it. – Howard Hinnant Jun 04 '17 at 03:25