多執行緒程式設計中,正確選擇與使用同步原語是避免競態與死鎖的關鍵。本文系統比較 POSIX 執行緒鎖(mutex)、條件變數(cond)與信號量(semaphore),並提供可編譯執行的生產者-消費者完整範例。
用於「等待特定條件成立」,而非單純互斥。核心模式:
// 生產者:設定條件並通知
pthread_mutex_lock(&mutex);
condition = true;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
// 消費者:等待條件成立
pthread_mutex_lock(&mutex);
while (!condition) // 必須用 while 防止虛假喚醒
pthread_cond_wait(&cond, &mutex);
condition = false; // 消費條件
pthread_mutex_unlock(&mutex);
pthread_cond_wait 可能被虛假喚醒(spurious wakeup),醒來必須重新判斷條件。pthread_cond_wait 內部原子執行「解鎖 → 等待 → 加鎖」,確保條件檢查與等待無縫衔接。計數器抽象,可作二元鎖(0/1)或資源計數器:
sem_wait():計數器 -1,若為 0 則阻塞。sem_post():計數器 +1,喚醒一個等待執行緒。sem_getvalue():讀取當前計數。// 模擬 mutex:初始值 1
sem_t mutex; sem_init(&mutex, 0, 1);
sem_wait(&mutex); // lock
// critical section
sem_post(&mutex); // unlock
// 模擬 cond:生產者 post,消費者 wait
sem_t cond; sem_init(&cond, 0, 0);
// consumer: sem_wait(&cond);
// producer: sem_post(&cond);
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
class CThreadQueue {
pthread_mutex_t mux;
pthread_cond_t condGet, condPut;
void **buffer;
int sizeQueue, lput, lget, nFullThread, nEmptyThread, nData;
public:
CThreadQueue(int sz=1024): sizeQueue(sz), lput(0), lget(0),
nFullThread(0), nEmptyThread(0), nData(0) {
pthread_mutex_init(&mux, 0);
pthread_cond_init(&condGet, 0);
pthread_cond_init(&condPut, 0);
buffer = new void*[sizeQueue];
}
~CThreadQueue() { delete[] buffer; }
void *getq() {
void *data;
pthread_mutex_lock(&mux);
while (lget == lput && nData == 0) {
nEmptyThread++;
pthread_cond_wait(&condGet, &mux);
nEmptyThread--;
}
data = buffer[lget++];
if (lget == sizeQueue) lget = 0;
nData--;
if (nFullThread) pthread_cond_signal(&condPut);
pthread_mutex_unlock(&mux);
return data;
}
void putq(void *data) {
pthread_mutex_lock(&mux);
while (lput == lget && nData) {
nFullThread++;
pthread_cond_wait(&condPut, &mux);
nFullThread--;
}
buffer[lput++] = data;
if (lput == sizeQueue) lput = 0;
nData++;
if (nEmptyThread) pthread_cond_signal(&condGet);
pthread_mutex_unlock(&mux);
}
};
// 測試主程式略...
sem_t *mux, *get, *put 替換 mutex/cond。sem_wait(mux) 替代 pthread_mutex_lock,sem_post(mux) 替代 unlock。sem_wait(get) 替代 pthread_cond_wait(&condGet, &mux),注意:信號量無內建 mutex 綁定,需自行確保檢查條件與等待的原子性(通常仍需一把 mutex 保護佇列狀態,信號量僅用於通知)。| 場景 | 推薦 | 理由 |
|---|---|---|
| 單純互斥 | mutex | 語意清晰、所有權檢查、優先權繼承支援 |
| 等待條件/事件 | cond + mutex | 條件檢查與等待原子結合,避免競態 |
| 資源計數/跨行程同步 | semaphore | 計數語意天然,支援 process-shared |
| 簡單通知(無資料保護) | semaphore 或 eventfd | 輕量、無擁有權限制 |
掌握三者語意差異與組合模式,能寫出正確、高效且可維護的並發程式。