#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#define BUFFER_SIZE 10
#define PRODUCER_THREADS 3
#define CONSUMER_THREADS 2
int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
sem_t empty;
sem_t full;
pthread_mutex_t mutex;
// 生产者线程函数
void* producer(void* arg) {
int id = *((int*)arg);
while (1) {
int num = rand() % 100; // 生成随机整数
sem_wait(&empty); // 等待缓冲区有空闲位置
pthread_mutex_lock(&mutex);
buffer[in] = num;
printf("Producer %d produced %d at position %d\n", id, num, in);
in = (in + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&mutex);
sem_post(&full); // 通知缓冲区有新数据
}
return NULL;
}
// 消费者线程函数
void* consumer(void* arg) {
int id = *((int*)arg);
while (1) {
sem_wait(&full); // 等待缓冲区有数据
pthread_mutex_lock(&mutex);
int num = buffer[out];
printf("Consumer %d consumed %d from position %d, squared value: %d\n", id, num, out, num * num);
out = (out + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&mutex);
sem_post(&empty); // 通知缓冲区有空闲位置
}
return NULL;
}
int main() {
pthread_t producers[PRODUCER_THREADS];
pthread_t consumers[CONSUMER_THREADS];
int producer_ids[PRODUCER_THREADS];
int consumer_ids[CONSUMER_THREADS];
sem_init(&empty, 0, BUFFER_SIZE);
sem_init(&full, 0, 0);
pthread_mutex_init(&mutex, NULL);
srand(time(NULL));
for (int i = 0; i < PRODUCER_THREADS; i++) {
producer_ids[i] = i;
pthread_create(&producers[i], NULL, producer, &producer_ids[i]);
}
for (int i = 0; i < CONSUMER_THREADS; i++) {
consumer_ids[i] = i;
pthread_create(&consumers[i], NULL, consumer, &consumer_ids[i]);
}
for (int i = 0; i < PRODUCER_THREADS; i++) {
pthread_join(producers[i], NULL);
}
for (int i = 0; i < CONSUMER_THREADS; i++) {
pthread_join(consumers[i], NULL);
}
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&mutex);
return 0;
}
如何避免死锁
- 顺序加锁:在上述代码中,生产者和消费者线程都是先获取
empty
或full
信号量,再获取互斥锁mutex
,解锁时顺序相反。这保证了所有线程加锁的顺序一致,避免死锁。
- 资源分配图算法:可以使用如银行家算法等资源分配图算法来检测和避免死锁,但在这种简单场景下,上述顺序加锁的方式足以避免死锁。
- 避免嵌套锁:尽量减少锁的嵌套使用。如果必须嵌套,严格按照相同顺序获取锁。在本程序中,只有一个互斥锁
mutex
,且获取和释放逻辑简单清晰,减少了死锁风险。