Is it safe to use volatile bool to make another thread wait? (C ++)

All that I read about volatile says that it is never safe, but I still feel inclined to try it, and I did not see that this particular scenario was declared unsafe.

I have a separate stream that displays the scene, pulling data from the main modeling stream. It does not sync and works fine.

The problem is that when the program exits the system, then the visualizer must stop pulling data from the modeling stream before the modeling stream can safely clear itself, without forcing the rendering to try to read invalid memory.

To accomplish this, I have a visualization tool endlessly in my stream:

volatile bool stillRendering; void RenderThreadFunction() { stillRendering = true; while(programRunning) { renderer->render(); } stillRendering = false; } 

In the main thread of the program, when the windproc exit message is received, I:

 void OnQuit() { programRunning = false; while(stillRendering) { } delete application; } 

The goal is to ensure that the visualizer stops retrieving data from the application before calling delete in the application.

At first I tried this without any volatile keywords, and it worked in debug mode, but in release mode it hung. I assume the compiler has done some optimization, which causes the program to stop checking the stillRendering value.

Adding volatility to a simple display that caused the application to complete successfully when I tested it so far. I am not sure why this does not matter if "programRunning" is unstable.

Finally, I do not know how the use of volatile for "stillRendering" will affect program performance. It doesn't matter to me if doing stillRendering volatile affects the performance of OnQuit (), but to me it matters if it affects the performance of RenderThreadFunction ()

+8
c ++ multithreading thread-safety volatile boolean
source share
5 answers

This is completely unsafe, although it may work with some compilers. Basically, volatile only affects the variable attached to, so RendererThreadFunction , for example, can set stillRendering false until renderer->render(); . (This is true even if both stillRendering and programRunning were volatile.) The likelihood of a problem is very small, so testing may not open it. And finally, some versions of VC ++ give volatile semantics of atomic access in C ++ 11, in which case your code will work. (Until you compile another version of VC ++, of course.)

Given that renderer->render() almost certainly takes a non-negligible amount of time, there is absolutely no reason for using a conditional variable here. About the only time you would use volatile for this kind of thing, if the shutdown mechanism was triggered by a signal (in this case the type would be sig_atomic_t , not bool , although in practice this probably does not matter). In this case there will be not two streams, but only a rendering stream and a signal handler.

+8
source share

If you want your code to work on all architectures in all compilers, use C ++ 11 atomics:

 std::atomic<bool> stillRendering; void RenderThreadFunction() { stillRendering = true; while(programRunning) { renderer->render(); } stillRendering = false; } 

Volatile is not intended for use with multithreading - compilers are actually allowed by the standard to reorder volatile access with access without volatile . VC ++ extends the range of volatile functions to prevent reordering, but other compilers do not, and this may break on these compilers.

As others noted, volatile also does not affect visibility, that is, architectures that are not cache-coherent will never be able to see the flag set. x86 is not even immediately cache-coherent (records will be very slow), so your program will end up looping more than necessary while the record is being sent through various buffers.

C ++ 11 atoms avoid both of these problems.

OK, so this was mainly intended to fix your current code and warn against the misuse of volatile . James's suggestion to use a condition variable (which is just a more efficient version of what you are doing) is probably the best solution for you.

+6
source share

There are three problems that address atomic atoms of C ++ 11.

Firstly, a thread switch may occur in the middle of a read or write value; for reading, another thread may update the value before the source thread reads the rest of the value; for recording, another stream could see a semi-recorded value. This is called a "gap."

Secondly, in a typical multiprocessor system, each processor has its own cache, and it reads and writes values ​​to this cache; sometimes the cache and main memory are updated to ensure that they contain the same values, but while the processor that writes the new value does not flush its cache, and the thread that reads the value reloads its copy from the cache, the value may be different . This is called "cache coherency."

Third, the compiler can move the code and store one value before it saves another, even if the code is written in reverse order. As long as you cannot write a valid program that can see the difference, this is normal under the β€œas if” rule.

Loading and saving to an atomic variable (with default ordering) prevents all three problems. Mark variable as volatile no.

EDIT: Don't worry about which architectures are causing the problems. The author of the standard library has already done this for the architecture for which the library implementation is intended. Do not look for shortcuts; just use atoms. You will not lose anything.

+4
source share

Depending on the architecture, which is cache coherent (e.g. x86 processors), I expect this to work fine. You may find that either of your two threads can iterate more than if you use true atomic operations, but since only one side sets or reads values, there is no requirement for true atomic operations.

However, if the processor (s) executing the code requires special cache flushing so that the "other core (s)" displays the updated value, then you may get stuck for a while - and you will need proper atomic updates to ensure that the cache is different The processor is invalid.

I assume that renderer->render() takes a lot of time, so reading stillRendering should not greatly affect the overall execution time. volatile usually means "please do not put this in a register or store it there."

(You probably need programRunning be volatile !)

+2
source share

Adding volatile to just stillRendering made the application successfully complete every time I tested it

Yes, your script will work.

A common mistake that occurs when using volatile variables to synchronize threads is that operations with volatile variables are assumed to be atomic . This is not true.

In your case, you will poll one bool, expecting it to change exactly once to zero. You don't seem to expect any operation to be atomic. On the other hand, even if you polled a single int , C ++ does not guarantee that a thread modifying this int will do it atomically.

I am not sure why this does not matter if "programRunning" is unstable.

Never mind. Make it volatile .

Creating a volatile variable ensures that cache optimization is eliminated, and that is what you want.

This does not mean that you are guaranteed the same cache optimizations when the variable is unstable. You just let the compiler decide. And at this particular moment, the compiler, it turns out, makes a decision that works for you.

Finally, I'm not sure how the use of volatile for "stillRendering" will affect program performance.

Your impact is likely to be affected by this:

 while(stillRendering) { } 

You ask for one thread (possibly one whole CPU core) endlessly, without rest, read one variable.

Consider adding a sleep challenge during a cycle.

+1
source share

All Articles