生產者-消費者模型:共享緩衝區(倉庫),生產者放入產品,消費者取出。需滿足:
| 信號量 | 初值 | 用途 |
|---|---|---|
| p_sem (空位) | TOTAL_NUM - INIT_NUM | 生產者等待空位 |
| c_sem (產品) | INIT_NUM | 消費者等待產品 |
| sh_sem (互斥) | 1 | 保護 num 變量 |
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
#define PRODUCT_SPEED 3
#define CONSUM_SPEED 1
#define INIT_NUM 3
#define TOTAL_NUM 10
sem_t p_sem, c_sem, sh_sem;
int num = INIT_NUM;
void product(void) { sleep(PRODUCT_SPEED); }
int add_to_lib() { num++; usleep(500000); return num; }
void consum(void) { sleep(CONSUM_SPEED); }
int sub_from_lib() { num--; usleep(500000); return num; }
void* productor(void* arg) {
while (1) {
sem_wait(&p_sem); // 等待空位
product(); // 生產耗時
sem_wait(&sh_sem); // 互斥進入
printf("push into! total_num=%d\n", add_to_lib());
sem_post(&sh_sem); // 釋放互斥
sem_post(&c_sem); // 通知有產品
}
}
void* consumer(void* arg) {
while (1) {
sem_wait(&c_sem); // 等待產品
sem_wait(&sh_sem); // 互斥進入
printf("pop out! total_num=%d\n", sub_from_lib());
sem_post(&sh_sem); // 釋放互斥
sem_post(&p_sem); // 通知有空位
consum(); // 消費耗時
}
}
int main() {
pthread_t tid1, tid2;
sem_init(&p_sem, 0, TOTAL_NUM - INIT_NUM);
sem_init(&c_sem, 0, INIT_NUM);
sem_init(&sh_sem, 0, 1);
pthread_create(&tid1, NULL, productor, NULL);
pthread_create(&tid2, NULL, consumer, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
gcc -o prod_cons prod_cons.c -lpthread
./prod_cons
| 錯誤 | 後果 | 修正 |
|---|---|---|
| 互斥信號量放在生產/消費函數外 | 並發度降低,長時間持有鎖 | 僅保護共享變量訪問 |
| wait 順序顛倒 | 可能死鎖 | 固定順序:資源信號量 → 互斥信號量 |
| 忘記 post | 線程永久阻塞 | 每個 wait 配對 post |
| 使用二元信號量代替計數信號量 | 無法表達多個空位/產品 | 正確使用計數信號量 |
三信號量方案清晰分離「資源可用性」(空位/產品)與「互斥訪問」,是生產者-消費者問題的教科書級解法。掌握信號量初始化、P/V 配對、鎖粒度控制,是 Linux 多線程開發核心技能。