Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 多執行緒同步機制深度解析:鎖、條件變數與信號量

編輯:Linux 教學

多執行緒程式設計中,正確選擇與使用同步原語是避免競態與死鎖的關鍵。本文系統比較 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);

關鍵規則

  1. while 迴圈檢查:pthread_cond_wait 可能被虛假喚醒(spurious wakeup),醒來必須重新判斷條件。
  2. 原子解鎖等待再加鎖:pthread_cond_wait 內部原子執行「解鎖 → 等待 → 加鎖」,確保條件檢查與等待無縫衔接。
  3. 傳入已加鎖的 mutex:呼叫前必須持有鎖,否則行為未定義。

三、信號量

計數器抽象,可作二元鎖(0/1)或資源計數器:

  • sem_wait():計數器 -1,若為 0 則阻塞。
  • sem_post():計數器 +1,喚醒一個等待執行緒。
  • sem_getvalue():讀取當前計數。

模擬 mutex 與 cond

// 模擬 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);

完整範例:環狀佇列(Mutex + 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輕量、無擁有權限制

常見陷阱提醒

  • 忘記 while 迴圈 → 虛假喚醒導致邏輯錯誤。
  • cond_wait 未持有鎖 → 未定義行為,極難重現。
  • 信號量替代 cond 但未保護共享資料 → 競態條件。
  • 鎖順序不一致 → 死鎖,建立全域鎖層級規範。

掌握三者語意差異與組合模式,能寫出正確、高效且可維護的並發程式。

Copyright © Windows教程網 All Rights Reserved