首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux C 多線程同步經典實戰:信號量實現生產者-消費者模型

     編輯:關於C語言

問題描述

生產者-消費者模型:共享緩衝區(倉庫),生產者放入產品,消費者取出。需滿足:

  • 緩衝區滿時生產者等待
  • 緩衝區空時消費者等待
  • 互斥訪問緩衝區計數器

三信號量方案

信號量初值用途
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

關鍵點解析

  1. 信號量初始化:`sem_init(&sem, 0, value)`,第 2 參數 0 表示線程間共享
  2. P 操作 (wait):原子性減 1,值 < 0 時阻塞
  3. V 操作 (post):原子性加 1,喚醒等待線程
  4. 互斥信號量:保護 `num` 讀寫,防止競態條件
  5. 順序關鍵:生產者先 `wait(p_sem)` 再 `wait(sh_sem)`;消費者先 `wait(c_sem)` 再 `wait(sh_sem)`。避免死鎖

常見錯誤與修正

錯誤後果修正
互斥信號量放在生產/消費函數外並發度降低,長時間持有鎖僅保護共享變量訪問
wait 順序顛倒可能死鎖固定順序:資源信號量 → 互斥信號量
忘記 post線程永久阻塞每個 wait 配對 post
使用二元信號量代替計數信號量無法表達多個空位/產品正確使用計數信號量

擴展方向

  • 多生產者/多消費者:相同代碼,創建多線程即可
  • 環形緩衝區:用陣列 + head/tail 指標替換單一計數器
  • 條件變量實現:`pthread_cond_wait` + `pthread_mutex_t` 替代信號量
  • 無鎖隊列:原子操作實現高性能併發隊列

總結

三信號量方案清晰分離「資源可用性」(空位/產品)與「互斥訪問」,是生產者-消費者問題的教科書級解法。掌握信號量初始化、P/V 配對、鎖粒度控制,是 Linux 多線程開發核心技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved