views:

118

answers:

5

In a multithreaded C++ program, I have the equivalent of this running in one thread:

while(obj->member) { } // waiting for obj->member to be set to false in another thread

and in another thread, obj->member is set to false. Even when it's set to false, however, the loop doesn't break. If I change it to this:

while(obj->member) { Sleep(1) }

It works as expected and breaks when obj->member is set to false in the other thread.
Why does it work like this?

+6  A: 

Obj must be declared volatile. That tells the compiler that it's value may change by some other thread.

When you add the Sleep, the compiler knows other threads are at work, and assumes that it might change.

James Curran
+9  A: 

Try making member volatile. This will force it to be fetched from memory each time it is used, rather than from a CPU register (which is how the compiler might optimise it.)

Mark H
+4  A: 

The fact that it works is mostly accidental. If you want to do things like this dependably, you just about need to use some sort of OS-supplied IPC mechanism, possibly with Boost Interprocess (e.g., mutex or semaphore) to give a more portable front end.

volatile, while often put forward as a solution to problems like this, is neither necessary nor sufficient. It can hurt performance (a lot) but still isn't enough to make threading work correctly. If you're programing for .NET, Microsoft has defined its version of volatile to provide (at least some degree of) thread safety. Otherwise (in real C or C++), it's of little real use, and can cause considerable harm.

I should also mention that this is a long ways from the first time this mistake has been made. Way back when, no less an authority than Andre Alexandrescu wrote a fairly substantial article in Doctor Dobbs about using volatile for threading. He's since realized that he was wrong, but (to a large extent) the damage was done -- especially since volatile is oh so very close to doing the right things that it's extremely easy to mistake it for being right/useful in threading.

Jerry Coffin
A: 

Perhaps your threading library is making a copy of the object upon thread creation and the member is not really shared among threads unless it is declared static.

No, it's a pointer to an object.
Hock
+1  A: 

A better way to do this would be to use an event instead of the bool. With an event, you can wait on it without using any CPU, and you will not have to sleep or use volitile.

Mike