讀者-寫者問題的變體:多生產者向共享緩衝區寫入數據,多消費者讀取。核心約束:
typedef struct {
void **buffer; // 數據指針數組
size_t capacity; // 隊列容量
size_t size; // 當前元素數
size_t head; // 讀指針
size_t tail; // 寫指針
pthread_mutex_t mutex; // 互斥鎖
sem_t empty_slots; // 空槽位信號量
sem_t filled_slots; // 已填槽位信號量
} circular_queue_t;
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define QUEUE_CAPACITY 10
#define NUM_PRODUCERS 2
#define NUM_CONSUMERS 3
#define ITEMS_PER_PRODUCER 20
typedef struct {
int *buffer[QUEUE_CAPACITY];
size_t head, tail, count;
pthread_mutex_t mutex;
sem_t empty, full;
} circle_queue_t;
circle_queue_t g_queue = {
.head = 0, .tail = 0, .count = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
// 信號量需運行時初始化
};
void queue_init(circle_queue_t *q) {
sem_init(&q->empty, 0, QUEUE_CAPACITY); // 初始全空
sem_init(&q->full, 0, 0); // 初始無數據
}
void queue_destroy(circle_queue_t *q) {
sem_destroy(&q->empty);
sem_destroy(&q->full);
pthread_mutex_destroy(&q->mutex);
}
int enqueue(circle_queue_t *q, int *item) {
sem_wait(&q->empty); // 等待空槽 (P操作)
pthread_mutex_lock(&q->mutex); // 獲取互斥鎖
q->buffer[q->tail] = item;
q->tail = (q->tail + 1) % QUEUE_CAPACITY;
q->count++;
pthread_mutex_unlock(&q->mutex); // 釋放互斥鎖
sem_post(&q->full); // 通知有數據 (V操作)
return 0;
}
int dequeue(circle_queue_t *q, int **item) {
sem_wait(&q->full); // 等待數據
pthread_mutex_lock(&q->mutex);
*item = q->buffer[q->head];
q->head = (q->head + 1) % QUEUE_CAPACITY;
q->count--;
pthread_mutex_unlock(&q->mutex);
sem_post(&q->empty); // 通知有空槽
return 0;
}
void *producer(void *arg) {
int id = *(int*)arg;
for (int i = 0; i < ITEMS_PER_PRODUCER; i++) {
int *item = malloc(sizeof(int));
*item = id * 1000 + i; // 編碼生產者ID和序號
enqueue(&g_queue, item);
printf("Producer %d: produced %d\n", id, *item);
usleep(rand() % 100000); // 模擬生產耗時
}
return NULL;
}
void *consumer(void *arg) {
int id = *(int*)arg;
while (1) {
int *item;
dequeue(&g_queue, &item);
printf("Consumer %d: consumed %d\n", id, *item);
free(item); // 消費後釋放
usleep(rand() % 150000); // 模擬消費耗時
}
return NULL;
}
int main() {
queue_init(&g_queue);
srand(time(NULL));
pthread_t producers[NUM_PRODUCERS], consumers[NUM_CONSUMERS];
int prod_ids[NUM_PRODUCERS], cons_ids[NUM_CONSUMERS];
for (int i = 0; i < NUM_PRODUCERS; i++) {
prod_ids[i] = i;
pthread_create(&producers[i], NULL, producer, &prod_ids[i]);
}
for (int i = 0; i < NUM_CONSUMERS; i++) {
cons_ids[i] = i;
pthread_create(&consumers[i], NULL, consumer, &cons_ids[i]);
}
for (int i = 0; i < NUM_PRODUCERS; i++)
pthread_join(producers[i], NULL);
// 給消費者處理剩餘數據的時間
sleep(2);
queue_destroy(&g_queue);
return 0;
}
信號量 empty (初始=容量) 信號量 full (初始=0) 互斥鎖 mutex
↑ ↑ ↑
生產者: 消費者: 保護:
P(empty) --等待空槽--> P(full) --等待數據--> lock
讀取數據 修改隊列
V(full) --通知有數據--> V(empty) --通知有空槽--> unlock
| 決策點 | 選擇 | 理由 |
|---|---|---|
| 同步原語 | 信號量 + 互斥鎖 | 信號量處理資源計數,互斥鎖保護臨界區,職責分離 |
| 隊列類型 | 循環數組 | O(1) 入隊出隊,無內存碎片,緩存友好 |
| 數據所有權 | 生產者分配,消費者釋放 | 明確所有權轉移,避免雙重釋放/洩漏 |
| 忙等待 vs 阻塞 | 信號量阻塞 | CPU 效率高,無自旋浪費 | 批量操作 | 一次 enqueue/dequeue 多個項目,減少系統調用開銷 | 優先級繼承 | 避免優先級反轉,實時系統必備 | 內存池 | 預分配項目內存,消除 malloc/free 開銷 |