Thread Synchronization 互動教學
🔒 Mutex 互動體驗:手動控制 Lock/Unlock
互動方式:你可以手動控制兩個執行緒的每個動作!
使用 Mutex:體驗「誰 lock 誰才能 unlock」,其他執行緒必須等待。
不使用 Mutex:兩個執行緒同時操作共享變數,會發生 Race Condition 導致計數錯誤!
🔑 Mutex Lock/Unlock 關鍵重點
- k_mutex_lock() - 嘗試「取得鎖」,如果已被佔用就無法執行(按鈕會被禁用)
- 臨界區 (Critical Section) - 只有持有鎖的執行緒才能安全執行 counter++
- k_mutex_unlock() - 只有「持有者」才能釋放,其他執行緒的 unlock 按鈕會被禁用
- 擁有權原則 - 誰 lock 就必須誰 unlock(不能由其他執行緒代勞)
🎮 模式選擇:
當前模式:✅ 使用 Mutex 保護
⚠️ Race Condition 發生!兩個執行緒同時讀取相同值,導致其中一次 +1 失效!
共享計數器 (Shared Counter): 0
程式碼範例
static volatile uint32_t counter = 0; K_MUTEX_DEFINE(counter_lock); void thread_function() { k_mutex_lock(&counter_lock, K_FOREVER); counter++; k_mutex_unlock(&counter_lock); }
📡 Semaphore 示範 A:ISR → 執行緒通知
情境:模擬每秒的 Timer ISR 發出通知,工作執行緒收到後才處理取樣。
重點:k_sem_give() 可在 ISR 呼叫,而 Mutex 不行!
已處理取樣: 0
程式碼範例
K_SEM_DEFINE(sample_sem, 0, 1); static void timer_handler() { k_sem_give(&sample_sem); } void worker() { while (1) { k_sem_take(&sample_sem, K_FOREVER); process_sampling(); } }
🎯 Semaphore 示範 B:資源池(最多 3 個)
情境:系統同時最多允許 3 個 DMA buffer 被使用。
第 4 個嘗試:會阻塞等待,直到有人釋放資源。
可用資源: 3 / 3
程式碼範例
K_SEM_DEFINE(buf_sem, 3, 3); void use_buffer() { k_sem_take(&buf_sem, K_FOREVER); do_work(); k_sem_give(&buf_sem); }
📊 Semaphore vs Mutex 比較表
| 項目 |
Semaphore(訊號量) |
Mutex(互斥鎖) |
| 本質 |
一個計數器(可設定最大值) |
一把鎖(一次只能一個持有者) |
| 擁有權 |
無擁有者:A take,B 也能 give |
有擁有者:誰 lock 就必須誰 unlock |
| 主要用途 |
1) 事件/通知
2) 資源池管理 |
保護臨界區(同時僅一執行緒可進入) |
| ISR 使用 |
✅ k_sem_give() 可在 ISR 呼叫 |
❌ 不能在 ISR lock/unlock |
| 優先權反轉 |
沒有內建處理機制 |
✅ Zephyr 具優先權繼承 |
| Race Condition |
無法防止(設計目的不同) |
✅ 可防止多執行緒同時存取共享資源 |
🎯 選擇決策
- 保護共享資料的臨界區 → 用 Mutex
- ISR/裝置 → 執行緒的喚醒/通知 → 用 Semaphore
- 固定數量資源池 → 用 Counting Semaphore
- 千萬別在 ISR 用 mutex;也不要用 semaphore 當互斥鎖