Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

คำสั่ง volatile ใน C++: การใช้งานกับ Hardware และ ISR

volatile ใน C++ มีความหมายเหมือน C: บอก Compiler ว่าตัวแปรอาจเปลี่ยนแปลงได้ "นอกเหนือจาก Control Flow ของโปรแกรม" จึงต้องไม่ Optimize การ Access

Syntax


volatile int flag;
int volatile flag;           // เทียบเท่า
volatile int* ptr;           // Pointer to volatile int
int* volatile ptr;           // Volatile pointer to int
volatile int* volatile ptr;  // ทั้งคู่

Use Cases ที่ถูกต้อง

  1. Memory-Mapped Hardware Registers: Address ที่ Map เข้ากับ Hardware Device
  2. Variables แชร์กับ Interrupt Service Routines (ISR): Flag, Counter
  3. Variables ใน setjmp/longjmp Context: ป้องกัน Register Caching
  4. Signal Handlers (C): volatile sig_atomic_t

ตัวอย่าง: Hardware Register


// STM32 GPIO Example
#define GPIOA_BASE 0x48000000
#define GPIO_ODR_OFFSET 0x14

volatile uint32_t* GPIOA_ODR = (volatile uint32_t*)(GPIOA_BASE + GPIO_ODR_OFFSET);

void setPin(int pin) {
    *GPIOA_ODR |= (1U << pin);  // Compiler ต้อง Generate STR Instruction
}

int readPin(int pin) {
    return (*GPIOA_ODR >> pin) & 1U; // Compiler ต้อง Generate LDR Instruction
}

ตัวอย่าง: ISR Flag


#include <atomic> // สำหรับ Context อื่น

volatile bool timerExpired = false; // ISR เขียน, Main อ่าน

extern "C" void TIM2_IRQHandler() { // ISR
    timerExpired = true;
}

int main() {
    while (!timerExpired) { // Compiler ไม่ Cache timerExpired ใน Register
        // Do work
    }
    // Timer Expired
}

⚠️ volatile ≠ Thread Synchronization

Featurevolatilestd::atomic
Compiler Optimization Barrier✅✅
Atomicity (Indivisible Operations)❌✅
Memory Ordering (Happens-Before)❌✅
Cache Coherence❌✅ (Hardware)
Use CaseHardware, ISR, setjmpThread Communication

ผิด: ใช้ volatile สำหรับ Thread Flag


// ❌ WRONG: Data Race, No Memory Ordering
volatile bool ready = false;
int data = 0;

void producer() {
    data = 42;
    ready = true; // Store ไม่มี Release Semantics
}

void consumer() {
    while (!ready) {} // Load ไม่มี Acquire Semantics
    use(data);      // อาจเห็น data = 0 (Reordering)!
}

ถูกต้อง: ใช้ std::atomic


// ✅ CORRECT
std::atomic ready{false};
int data = 0;

void producer() {
    data = 42;
    ready.store(true, std::memory_order_release);
}

void consumer() {
    while (!ready.load(std::memory_order_acquire)) {}
    use(data); // รับประกันว่าเห็น data = 42
}

สรุป

volatile ใช้สำหรับ Hardware Access, ISR, setjmp/longjmp เท่านั้น ห้ามใช้สำหรับ Thread Synchronization (Data Race, No Memory Order) ใช้ std::atomic, std::mutex, std::condition_variable แทนสำหรับ Multi-threading