經典並發模式:生產者向共享緩衝區放入數據,消費者從中取出處理。核心約束:
Linux pthread 提供 pthread_mutex_t(互斥鎖)和 pthread_cond_t(條件變量)實現同步。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#define MAXLENGTH 10
typedef char *datatype;
typedef struct node {
datatype name;
struct node *next;
} node;
typedef struct queue {
node *front, *rear;
int len;
} queue;
void queue_init(queue *q) {
q->front = q->rear = NULL;
q->len = 0;
}
void queue_put(queue *q, datatype new_name) {
node *mynode = malloc(sizeof(node));
mynode->name = new_name;
mynode->next = NULL;
if (q->rear) q->rear->next = mynode;
q->rear = mynode;
if (q->front == NULL) q->front = mynode;
q->len++;
}
datatype queue_get(queue *q) {
node *mynode = q->front;
datatype myname = mynode->name;
q->front = q->front->next;
q->len--;
free(mynode);
return myname;
}
void queue_print(queue *q) {
node *tmp = q->front;
while (tmp) {
printf("%s ", tmp->name);
tmp = tmp->next;
}
printf("\n");
}
// 全局同步對象
pthread_cond_t q_not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t q_not_empty = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
void *producer(void *arg) {
queue *qt = (queue *)arg;
while (1) {
pthread_mutex_lock(&qlock);
// 緩衝區滿,等待 not_full 信號
while (qt->len >= MAXLENGTH) {
pthread_cond_wait(&q_not_full, &qlock);
}
queue_put(qt, "*"); // 生產數據
printf("生產: 長度=%d\n", qt->len);
pthread_mutex_unlock(&qlock);
pthread_cond_signal(&q_not_empty); // 通知消費者
usleep(100000); // 模擬生產耗時
}
return NULL;
}
void *consumer(void *arg) {
queue *qt = (queue *)arg;
while (1) {
pthread_mutex_lock(&qlock);
// 緩衝區空,等待 not_empty 信號
while (qt->len <= 0) {
pthread_cond_wait(&q_not_empty, &qlock);
}
datatype name = queue_get(qt); // 消費數據
printf("消費: %s, 剩餘長度=%d\n", name, qt->len);
pthread_mutex_unlock(&qlock);
pthread_cond_signal(&q_not_full); // 通知生產者
usleep(150000); // 模擬消費耗時
}
return NULL;
}
pthread_cond_wait 可能發生虛假喚醒,必須用 while 重新檢查條件。cond_wait 內部自動釋放鎖並休眠,喚醒後重新獲取鎖。signal 足夠;多生產/多消費建議 broadcast 避免信號丟失。PTHREAD_COND_INITIALIZER、PTHREAD_MUTEX_INITIALIZER 簡化代碼。
int main() {
pthread_t tid1, tid2;
queue *q = malloc(sizeof(queue));
queue_init(q);
pthread_create(&tid1, NULL, producer, q);
pthread_create(&tid2, NULL, consumer, q);
// 運行 5 秒後退出(演示用)
sleep(5);
printf("主線程退出\n");
return 0; // 實際項目需優雅關閉:設置標誌、broadcast、join
}
# 編譯(必須鏈接 pthread)
gcc -Wall -O2 -o prod_cons prod_cons.c -lpthread
# 運行
./prod_cons
volatile bool shutdown,生產者/消費者檢查後 break,主線程 pthread_cond_broadcast 喚醒所有等待線程,隨後 pthread_join 回收。__atomic / C11 atomic)實現無鎖 SPSC 隊列。PTHREAD_PRIO_INHERIT。| 現象 | 可能原因 | 排查方法 |
|---|---|---|
| 程序掛起 | 死鎖/信號未發送 | GDB 附加查看線程棧,檢查 cond_wait/cond_signal 配對 |
| 數據競爭 | 鎖保護範圍不足 | ThreadSanitizer (-fsanitize=thread) 檢測 |
| 內存洩漏 | 節點未 free | Valgrind --leak-check=full |
| 段錯誤 | 空指針解引用 | GDB bt,檢查 queue_get 前是否判空 |