volatile เป็น Type Qualifier บอก Compiler ว่า ค่าของตัวแปรอาจเปลี่ยนแปลงได้ทุกเมื่อ โดยกระบวนการภายนอกที่ Compiler ไม่รู้จัก** จึงต้องไม่ Optimize (Cache ใน Register, Reorder, Remove) การ Access ตัวแปรนี้
Syntax
volatile int flag;
int volatile flag; // เทียบเท่า
volatile int* ptr; // Pointer to volatile int
int* volatile ptr; // Volatile pointer to int (Pointer ตัวเอง Volatile)
volatile int* volatile ptr; // ทั้งคู่
เมื่อไหร่ต้องใช้ volatile?
| Scenario | ตัวอย่าง |
|---|---|
| Memory-Mapped I/O (Hardware Registers) | GPIO, UART, Timer Registers |
| Global Variables แชร์กับ Interrupt Service Routine (ISR) | Flag ที่ ISR Set, Main Loop Check |
| Global Variables แชร์ระหว่าง Threads (Basic) | Flag สัญญาณ (แต่ควร用 Atomic/Mutex) |
| setjmp/longjmp Context | Variables ที่ต้องคงค่าระหว่าง Jump |
ตัวอย่าง: Hardware Register
// Address 0x4000_0000 คือ GPIO Output Register
volatile uint32_t* GPIO_OUT = (volatile uint32_t*)0x40000000;
void set_pin(int pin) {
*GPIO_OUT |= (1 << pin); // Write: Compiler ต้อง Generate Write Instruction
}
int read_pin(int pin) {
return (*GPIO_OUT >> pin) & 1; // Read: Compiler ต้อง Generate Read Instruction
}
ตัวอย่าง: ISR Flag
volatile bool interruptFlag = false;
void ISR_Timer() { // Interrupt Handler
interruptFlag = true; // Write
}
int main() {
while (!interruptFlag) { // Read: Compiler ไม่ Cache ใน Register
// Wait...
}
// Interrupt Occurred
}
สิ่งที่ volatile ไม่ ทำ (Common Misconceptions)
| Misconception | Reality |
|---|---|
| ทำให้ Thread-Safe | ❌ ไม่ให้ Atomicity, Memory Ordering, Visibility -> ใช้ std::atomic |
| ป้องกัน Race Condition | ❌ ไม่มี Synchronization |
| บังคับ Flush Cache | ❌ Compiler Level เท่านั้น (CPU Cache ไม่เกี่ยว) |
| เหมือน Atomic | ❌ ต่างกันสิ้นเชิง |
volatile vs std::atomic (C++11)
// Shared Flag ระหว่าง Threads
volatile bool v_flag = false; // ❌ ผิด: No Memory Ordering
std::atomic<bool> a_flag{false}; // ✅ ถูกต้อง: Sequential Consistency
// Hardware Register
volatile uint32_t* hw_reg = ...; // ✅ ถูกต้อง: Compiler Barrier
std::atomic<uint32_t>* hw_reg_atomic = ...; // ❌ ผิด: Atomic Operations บน HW Register ไม่ได้
สรุป
volatile ใช้สำหรับ Hardware Registers, ISR Flags, setjmp/longjmp บอก Compiler อย่า Optimize Access ไม่ใช่ สำหรับ Thread Synchronization (ใช้ std::atomic, Mutex) การเข้าใจขอบเขตการทำงานสำคัญมากสำหรับ Embedded และ Systems Programming