現實中,順序執行是常態:我完成,你才能接著做。程式設計同理,線程同步的核心是協調對共享資源(記憶體區域、全局變量)或臨界區的訪問順序。當共享資源同一時刻只能容忍單線程操作(特別是寫操作)時,必須引入同步機制。Linux 提供兩大主流方案:信號量與互斥量。本文聚焦信號量。
信號量是一種特殊變量,支持原子性的增減操作。即使多線程並發修改,系統保證操作依次進行,避免競態條件。普通變量在並發下會導致未定義行為。
按取值範圍分類:
| 函數 | 功能 | 關鍵參數 |
|---|---|---|
sem_init(sem, pshared, value) | 初始化信號量 | pshared=0 進程內共享;value 初始值 |
sem_wait(sem) | 原子減 1(P 操作) | 值 > 0 時立即返回;值 = 0 時阻塞等待 |
sem_post(sem) | 原子加 1(V 操作) | 喚醒等待線程(如有) |
sem_getvalue(sem, &val) | 獲取當前值 | 非標準 POSIX,Linux 支持 |
sem_destroy(sem) | 銷毀信號量 | 若有線程等待則報錯 |
關鍵行為:sem_wait 會阻塞直到信號量值 ≥ 1,然後原子減 1 並繼續;sem_post 原子加 1 並喚醒一個等待線程(如有)。
設計邏輯:主線程初始化信號量為 0;讀取輸入後 sem_post;子線程 sem_wait 阻塞,被喚醒後處理數據,處理完畢再 sem_wait 等待下一次輸入。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
void *threadFunc(void *arg);
sem_t sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE]; // 共享緩衝區
int main() {
int res;
pthread_t upperThread;
void *threadRes;
res = sem_init(&sem, 0, 0); // 初始值 0
if (res != 0) { perror("Semaphore init failed"); exit(EXIT_FAILURE); }
res = pthread_create(&upperThread, NULL, threadFunc, NULL);
if (res != 0) { perror("Thread create failed"); exit(EXIT_FAILURE); }
printf("Please input convert text, then enter 'end' to finish.\n");
while (strncmp("end", work_area, 3) != 0) {
fgets(work_area, WORK_SIZE, stdin);
sem_post(&sem); // 輸入完成,釋放信號量
int semValue = 0;
sem_getvalue(&sem, &semValue);
printf("Semaphore count: %d\n", semValue);
}
printf("\nWaiting for thread to finish...\n");
res = pthread_join(upperThread, &threadRes);
if (res != 0) { perror("Thread join failed"); exit(EXIT_FAILURE); }
printf("Thread joined.\n");
sem_destroy(&sem);
exit(EXIT_SUCCESS);
}
void *threadFunc(void *arg) {
sem_wait(&sem); // 等待主線程輸入
int semValue = 0;
sem_getvalue(&sem, &semValue);
printf("Semaphore count: %d\n", semValue);
while (strncmp("end", work_area, 3) != 0) {
for (int i = 0; work_area[i] != '\0'; ++i) {
if (work_area[i] >= 'a' && work_area[i] <= 'z')
work_area[i] = work_area[i] - 32; // 轉大寫
}
printf("After converted: %s\n", work_area);
sem_wait(&sem); // 再次阻塞,等待下一次輸入
}
pthread_exit(NULL);
}
| 特性 | 互斥量 | 信號量 |
|---|---|---|
| 核心目的 | 互斥訪問臨界區 | 同步協調、資源計數 |
| 所有權 | 有擁有者(加鎖線程必須解鎖) | 無所有權(任意線程可 post) |
| 適用場景 | 保護共享數據結構 | 生產者-消費者、資源池、事件通知 |
| 計數能力 | 二元(鎖/解鎖) | 計數型,支持多實例 |
本例中,信號量單獨完成同步協調,但實際項目中常互斥量保護數據 + 信號量同步步驟組合使用(參考上一篇互斥量實戰代碼)。