The volatile keyword tells the compiler that a variable may change unexpectedly (for example, by hardware, another thread, or an interrupt handler), and prevents it from caching its value or optimizing accesses to it.
Used for:
volatile does not guarantee atomicity or synchronization!).volatile int flag = 0; void handler() { flag = 1; // interrupt handler } void loop() { while (!flag) { // waiting for an event } // ... }
Without volatile, the compiler might replace the loop with an infinite one (not reading flag from memory), with volatile, the variable is read from memory each time.
Is it enough to use volatile for correct information exchange between threads?
A common mistake is to assume that volatile provides memory synchronization between threads and makes operations atomic.
Correct answer:
volatile does not protect against data races in a multithreaded environment and does not provide memory barriers: it merely tells the compiler not to optimize the access. For guaranteed correctness, synchronization primitives (mutex, atomic operations, etc.) must be used.
// This is unsafe! volatile int ready = 0; void thread1() { data = 123; // write data ready = 1; // signal the other thread } void thread2() { while (!ready); // waiting for the event printf("data = %d\n", data); // data may not have updated yet! }
History
volatile was used for signaling, believing it was sufficient, only to encounter elusive bugs: sometimes a thread read outdated data or was in an inconsistent state — an atomic variable or mutex was necessary.