Does the C volatile keyword introduce a memory fence?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
No, volatile does not introduce a memory fence in normal C or C++ concurrency code. It tells the compiler that accesses to that object must actually occur, but it does not give you the ordering and synchronization guarantees that atomics and explicit fences provide.
What volatile Actually Means
volatile exists for cases where a value can change for reasons the compiler cannot see, such as memory-mapped device registers or variables touched by signal handlers or interrupts in specific environments. The important point is that it affects compiler optimization of that object, not the full cross-thread memory model.
A volatile read or write means “do not optimize away or merge this access as if nothing outside the current code can observe it.” That is much weaker than “make every thread see all prior writes in order.”
Why It Is Not a Memory Fence
A memory fence is about ordering. It prevents certain reads or writes from moving across a boundary from the point of view of other threads or cores. volatile does not provide that guarantee for ordinary multithreaded communication.
This broken example is a classic trap:
It may look like ready protects data, but it does not. There is still a data race on data, and volatile does not establish the ordering that would make this safe between threads.
Use Atomics for Thread Communication
For inter-thread signaling, use std::atomic in C++ or C11 atomics in C. With atomics, you can express both visibility and ordering:
The release store and acquire load form a synchronization relationship. Once the consumer sees ready == true, it is also guaranteed to observe the earlier write to data.
That is the kind of guarantee people often assume volatile gives them, but it does not.
What a Real Fence Looks Like
If you specifically need a fence rather than an atomic variable operation, C++ provides one explicitly:
A fence is a deliberate synchronization tool. It has ordering semantics by design. volatile is not a substitute for it.
Most code should prefer atomic loads and stores over manually sprinkling fences around, because it is easier to reason about a full atomic protocol than about separate fences and ordinary variables.
Where volatile Is Still Useful
This does not mean volatile is useless. It is appropriate when the problem is “this exact memory access must happen” rather than “threads must synchronize.”
Examples include:
- Reading a device register exposed through memory-mapped I/O
- Polling a hardware status flag in embedded code
- Some low-level signal or interrupt interactions in carefully controlled environments
Those are special cases. In mainstream multithreaded application code, volatile is usually the wrong tool for synchronization.
Common Pitfalls
The most common mistake is using volatile as if it were a lock-free thread communication primitive. It is not. It does not give atomicity, and it does not give the ordering guarantees needed for safe shared-state publication.
Another pitfall is mixing volatile variables with ordinary shared variables and assuming the volatile access makes the other variables safe too. It does not. The rest of the shared state still needs proper synchronization.
It is also easy to confuse compiler behavior with CPU behavior. Even if volatile changes how the compiler emits loads and stores for one object, that still does not mean the processor and memory subsystem will treat it as a full fence for surrounding operations.
Finally, avoid copying low-level embedded patterns into general server or desktop code without understanding the memory model. The correct primitive for threads is almost always std::atomic, a mutex, or another explicit synchronization tool.
Summary
- '
volatiledoes not introduce a memory fence for ordinary multithreaded code.' - It affects optimization of that object, not general thread synchronization.
- Use
std::atomicor C11 atomics for safe cross-thread communication. - Use explicit fences only when you truly need fence semantics.
- Keep
volatilefor hardware-facing or similarly specialized cases where actual access, not synchronization, is the requirement.
Related reading
- Does the Java Memory Model JSR-133 imply that entering a monitor flushes the CPU data caches?
- Does the SQL Server JDBC driver support asynchronous operations?
- Does the use of async/await create a new thread?
- Does the use of the Async suffix in a method name depend on whether the 'async' modifier is used?
- Does the range-based 'for' loop deprecate many simple algorithms?
- Duplicate a LinkedList with a pointer to a random node apart from the next node
- Doesn't Paxos end up with the same instructions in the exact same order?
- Doing an asynchronous HTTP request - what's the difference between these two?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.