经典生产者-消费者问题详解:使用信号量与互斥锁

2 下载量 93 浏览量 更新于2024-08-04 收藏 687KB DOC 举报
操作系统生产者和消费者问题是经典的并发控制问题,旨在解决多线程环境下共享资源的安全同步问题。这个问题最早由Dijkstra提出,作为其信号量机制的一个示范实例。问题的核心是设计一个包含一个生产者线程和一个消费者线程的程序,这两个线程共享一个或多个缓冲区。在这个场景中,我们假设有N个缓冲区(N通常是一个预设的常量,比如N=32)。 首先,为了确保线程安全,我们需要使用以下几种同步工具: 1. **互斥信号量(mutex)**:通过`pthread_mutex_tmutex`,这个互斥信号量保证任何时候只有一个线程可以访问缓冲区列表,防止数据竞争和不一致状态。 2. **空缓冲区信号量(empty semaphore)**:`sem_tempty_sem`用于当生产者线程试图向缓冲区添加新元素而所有缓冲区都已满时,阻止生产者继续,直到消费者线程消耗掉至少一个元素。 3. **满缓冲区信号量(full semaphore)**:`sem_tfull_sem`则是在消费者线程试图从缓冲区中取出元素,但所有缓冲区都没有空闲位置时,用来阻止消费者线程,直到生产者填充了至少一个空位。 下面是一个简单的C语言实现,展示了如何使用这些信号量来协调生产者和消费者的交互: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define M 32 /* 缓冲区数量 */ #define P(sem) sem_wait(&sem) // 生产者/消费者操作 #define V(sem) sem_post(&sem) // 生产者/消费者操作 int in = 0; // 生产者放置产品的索引 int out = 0; // 消费者取产品的位置 int buff[M] = {0}; // 初始化所有缓冲区为0 sem_t empty_sem; // 空缓冲区信号量 sem_t full_sem; // 满缓冲区信号量 pthread_mutex_t mutex; // 互斥锁 // 打印缓冲区内容 void print_buffer() { int i; for (i = 0; i < M; i++) { printf("%d", buff[i]); } } // 生产者函数,尝试生产并放入一个元素 void *producer(void *arg) { while (true) { P(mutex); // 获取互斥锁 if (buff[out] == 0) { // 如果缓冲区为空 buff[out] = 1; // 设置值 V(empty_sem); // 通知消费者缓冲区已满 } P(full_sem); // 等待消费者消费,如果满则继续等待 V(mutex); // 释放互斥锁 } return NULL; } // 消费者函数,尝试从缓冲区中取出一个元素 void *consumer(void *arg) { while (true) { P(mutex); // 获取互斥锁 if (buff[in] != 0) { // 如果有元素 buff[in] = 0; // 取消值 V(full_sem); // 通知生产者缓冲区已空 } P(empty_sem); // 等待生产者生产,如果空则继续等待 V(mutex); // 释放互斥锁 } return NULL; } int main() { sem_init(&empty_sem, 0, M); // 初始化空缓冲区信号量为M sem_init(&full_sem, 0, 0); // 初始化满缓冲区信号量为0 pthread_mutex_init(&mutex, NULL); pthread_t producer_thread, consumer_thread; pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); // ... 这里可以添加线程管理代码,如等待线程结束等 return 0; } ``` 在这个实现中,生产者和消费者线程通过循环不断进行,生产者只有在检测到空缓冲区后才会生产一个元素,并释放`empty_sem`;消费者也是如此,在发现非空缓冲区后消费并释放`full_sem`。这种机制确保了缓冲区资源的正确同步,避免了资源的竞争和死锁问题。