什麼是線程同步?
想象一下超市收銀台:如果所有顧客(線程)同時擠向同一個收銀台(共享資源),場面會一片混亂。線程同步就是給顧客們發"排隊號碼牌",確保:
-
- 有序訪問:每次只處理一個顧客
-
- 協調工作:收銀員(CPU)高效服務
-
- 避免衝突:防止算錯賬(數據錯誤)
Linux提供5種"排隊機制"解決多線程協作問題:
一、互斥鎖(單人洗手間規則)
#include <pthread.h>
// 創建鎖(相當於洗手間的"有人/無人"標識)
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void* thread_task(void* arg) {
// 嘗試進入(如果裏面有人就排隊等待)
pthread_mutex_lock(&lock);
/* 臨界區開始(你的"私人時間") */
printf("Thread %d using resource\n", *(int*)arg);
sleep(1); // 模擬耗時操作
/* 臨界區結束 */
// 開門出來(讓下一位使用)
pthread_mutex_unlock(&lock);
return NULL;
}
適用場景:
- 文件寫入操作
- 銀行賬户餘額修改
- 任何需要"獨享"資源的場景
特點:
- ✅ 簡單易用
- ⚠️ 過度使用會降低併發性能
二、條件變量(咖啡廳取餐系統)
pthread_cond_t order_ready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t counter_lock = PTHREAD_MUTEX_INITIALIZER;
int order_number = 0;
// 顧客線程(等待取餐)
void* customer(void* arg) {
pthread_mutex_lock(&counter_lock);
while(order_number == 0) { // 必須用while循環檢查
pthread_cond_wait(&order_ready, &counter_lock); // 放下鎖等待通知
}
printf("Got order %d!\n", order_number);
pthread_mutex_unlock(&counter_lock);
return NULL;
}
// 廚師線程(通知取餐)
void* chef(void* arg) {
sleep(2); // 模擬做飯時間
pthread_mutex_lock(&counter_lock);
order_number = 123;
pthread_cond_signal(&order_ready); // 叫號通知顧客
pthread_mutex_unlock(&counter_lock);
return NULL;
}
工作流程:
-
- 顧客:鎖定櫃枱 → 檢查訂單 → 等待叫號
-
- 廚師:完成訂單 → 鎖定櫃枱 → 更新訂單 → 發送通知
-
- 顧客:被喚醒 → 重新檢查 → 取餐
適用場景:
- 生產者-消費者模型(如消息隊列)
- 線程間任務協調
三、自旋鎖(搶車位)
pthread_spinlock_t parking_lock;
// 初始化鎖(停車場入口)
pthread_spin_init(&parking_lock, PTHREAD_PROCESS_PRIVATE);
void* driver(void* arg) {
// 開車繞圈找空位(CPU忙等待)
pthread_spin_lock(&parking_lock);
/* 停車成功(臨界區) */
printf("Car %d parked\n", *(int*)arg);
// 開走釋放車位
pthread_spin_unlock(&parking_lock);
return NULL;
}
適用場景:
- 極短操作(<0.1毫秒)
- 內核開發
- 實時系統
注意事項:
- ⚠️ 會浪費CPU資源
- ✅ 比互斥鎖響應更快
鎖類型對比表:
| 場景 | 推薦鎖類型 | 類比 |
|---|---|---|
| 短時間獨佔操作 | 自旋鎖 | 快速便利店購物 |
| 長時間獨佔操作 | 互斥鎖 | 餐廳包間用餐 |
| 多讀少寫 | 讀寫鎖 | 圖書館 |
| 線程組協調 | 屏障 | 旅行團集合 |
| 事件通知 | 條件變量 | 咖啡廳叫號系統 |
參考文章:Linux線程同步入門指南