To guarantee correctness in the context of inter-thread communication, prefer std::atomic with std::memory_order_relaxed to volatile T.
Join the DZone community and get the full member experience. There seems to be a lot of confusion about the significance of using std::atomic over a much more straightforward volatile T or T. In the context of concurrency, being aware of the difference between the three cases is one fundamental cornerstone in ensuring correctness. Consider the following sample: The code uses neither volatile nor std::atomic at this point and shares an integer variable shared with another thread. The expected order of events is as follows: The code is simple and works as expected in case of GCC and default options (corresponding to -O0). The test seems to indicate that the sample is correct but it is not. With -O3, the sample hangs indefinitely. Similar code could easily end up in production and might even run flawlessly for years. To get to the root cause, we have to inspect the generated assembly. First, the GCC 4.8.5 based x86_64 assembly corresponding to the working binary: Thread B executes a simple store of the value 42 in shared. Thread A reads shared for each loop iteration until the comparison indicates equality. Now, we compare that to the -O3 outcome: Optimizations associated with -O3 replaced the loop with a single comparison and, if not equal, an infinite loop to match the expected behavior. With GCC 10.2, the loop is optimized out. The problem is that the compiler and its optimizer are not aware of the implementation’s concurrency implications. Consequently, the conclusion needs to be that shared cannot change in thread A – the loop is equivalent to dead code. Data races are undefined behavior (UB), the optimizer is allowed to assume that the program doesn’t encounter UB. The solution requires us to communicate to the compiler that shared is involved in inter-thread communication. One way to accomplish that may be volatile. While the actual meaning of volatile varies across compilers and guarantees, if any, are compiler-specific, the general consensus is that volatile prevents the compiler from optimizing volatile accesses in terms of register-based caching.
Home
United States
USA — software What's the Difference Between T, Volatile T, and std::atomic in C++?