使用多线程/进程和System V信号量解决生产者消费者问题:有一个长度为N的缓冲池(共享内存)被生产者和消费者共同使用。只要缓冲池未满,生产者就可以将消息送入缓冲池;只要缓冲池不空,消费者便可从缓冲池中取走一
时间: 2024-05-10 21:18:22 浏览: 130
信号量来解决操作系统的消费者与生产者问题.rar_信号量_生产者 消费者_进程
条消息。为了避免生产者和消费者在同时访问缓冲池时出现竞争条件,我们可以使用多线程或进程以及 System V 信号量来解决生产者消费者问题。
使用多线程和信号量实现生产者消费者模型的伪代码如下:
```
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define N 10 // 缓冲池大小
#define MAX_MSG 100 // 最大消息数量
pthread_t tid_producer, tid_consumer; // 生产者和消费者线程 ID
sem_t sem_empty, sem_full, sem_mutex; // 信号量:空闲空间、已用空间、互斥量
int buffer[N]; // 缓冲池
int msg_count = 0; // 当前消息数量
void* producer(void* arg) {
int msg;
while (msg_count < MAX_MSG) {
msg = rand() % 1000; // 随机生成一个消息
sem_wait(&sem_empty); // 等待空闲空间
sem_wait(&sem_mutex); // 加锁
buffer[msg_count % N] = msg; // 将消息存入缓冲池
msg_count++;
printf("Producer produced message %d\n", msg);
sem_post(&sem_mutex); // 解锁
sem_post(&sem_full); // 发送已用空间信号
}
pthread_exit(NULL);
}
void* consumer(void* arg) {
int msg;
while (msg_count < MAX_MSG) {
sem_wait(&sem_full); // 等待已用空间
sem_wait(&sem_mutex); // 加锁
msg = buffer[(msg_count - 1) % N]; // 从缓冲池中取出消息
msg_count--;
printf("Consumer consumed message %d\n", msg);
sem_post(&sem_mutex); // 解锁
sem_post(&sem_empty); // 发送空闲空间信号
}
pthread_exit(NULL);
}
int main() {
sem_init(&sem_empty, 0, N); // 初始化空闲空间信号量
sem_init(&sem_full, 0, 0); // 初始化已用空间信号量
sem_init(&sem_mutex, 0, 1); // 初始化互斥信号量
pthread_create(&tid_producer, NULL, producer, NULL); // 创建生产者线程
pthread_create(&tid_consumer, NULL, consumer, NULL); // 创建消费者线程
pthread_join(tid_producer, NULL); // 等待生产者线程结束
pthread_join(tid_consumer, NULL); // 等待消费者线程结束
sem_destroy(&sem_empty); // 销毁空闲空间信号量
sem_destroy(&sem_full); // 销毁已用空间信号量
sem_destroy(&sem_mutex); // 销毁互斥信号量
return 0;
}
```
在上面的代码中,我们创建了三个 System V 信号量:
- `sem_empty`:空闲空间信号量,初始值为缓冲池大小 N。
- `sem_full`:已用空间信号量,初始值为 0。
- `sem_mutex`:互斥信号量,用于对缓冲池的访问进行加锁,初始值为 1。
在生产者线程中,我们使用 `sem_wait()` 函数等待空闲空间信号量,表示缓冲池中有空闲空间可以存放消息。然后使用 `sem_wait()` 函数对互斥信号量进行加锁,保证生产者线程与其他线程的访问是互斥的。接着,将消息存入缓冲池,并将消息数量加一。最后使用 `sem_post()` 函数释放互斥信号量和发送已用空间信号量,表示缓冲池中已经使用了一个空间。
在消费者线程中,我们使用 `sem_wait()` 函数等待已用空间信号量,表示缓冲池中有消息可以被消费者取出。然后使用 `sem_wait()` 函数对互斥信号量进行加锁,保证消费者线程与其他线程的访问是互斥的。接着,从缓冲池中取出消息,并将消息数量减一。最后使用 `sem_post()` 函数释放互斥信号量和发送空闲空间信号量,表示缓冲池中又有一个空间可以被生产者使用。
在主函数中,我们先进行信号量的初始化,然后创建生产者和消费者线程,并使用 `pthread_join()` 函数等待线程结束。最后销毁信号量并返回。
使用多进程和信号量实现生产者消费者模型的伪代码如下:
```
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <semaphore.h>
#define N 10 // 缓冲池大小
#define MAX_MSG 100 // 最大消息数量
sem_t *sem_empty, *sem_full, *sem_mutex; // 信号量:空闲空间、已用空间、互斥量
int* buffer; // 缓冲池
int msg_count = 0; // 当前消息数量
void producer() {
int msg;
while (msg_count < MAX_MSG) {
msg = rand() % 1000; // 随机生成一个消息
sem_wait(sem_empty); // 等待空闲空间
sem_wait(sem_mutex); // 加锁
buffer[msg_count % N] = msg; // 将消息存入缓冲池
msg_count++;
printf("Producer produced message %d\n", msg);
sem_post(sem_mutex); // 解锁
sem_post(sem_full); // 发送已用空间信号
}
}
void consumer() {
int msg;
while (msg_count < MAX_MSG) {
sem_wait(sem_full); // 等待已用空间
sem_wait(sem_mutex); // 加锁
msg = buffer[(msg_count - 1) % N]; // 从缓冲池中取出消息
msg_count--;
printf("Consumer consumed message %d\n", msg);
sem_post(sem_mutex); // 解锁
sem_post(sem_empty); // 发送空闲空间信号
}
}
int main() {
int shmid = shmget(IPC_PRIVATE, N * sizeof(int), 0666 | IPC_CREAT); // 创建共享内存
buffer = (int*) shmat(shmid, NULL, 0); // 将共享内存映射到进程地址空间
sem_empty = sem_open("/sem_empty", O_CREAT, 0644, N); // 创建空闲空间信号量
sem_full = sem_open("/sem_full", O_CREAT, 0644, 0); // 创建已用空间信号量
sem_mutex = sem_open("/sem_mutex", O_CREAT, 0644, 1); // 创建互斥信号量
pid_t pid_producer = fork(); // 创建生产者进程
if (pid_producer == 0) {
producer();
exit(0);
}
pid_t pid_consumer = fork(); // 创建消费者进程
if (pid_consumer == 0) {
consumer();
exit(0);
}
waitpid(pid_producer, NULL, 0); // 等待生产者进程结束
waitpid(pid_consumer, NULL, 0); // 等待消费者进程结束
sem_close(sem_empty); // 关闭空闲空间信号量
sem_close(sem_full); // 关闭已用空间信号量
sem_close(sem_mutex); // 关闭互斥信号量
sem_unlink("/sem_empty"); // 删除空闲空间信号量
sem_unlink("/sem_full"); // 删除已用空间信号量
sem_unlink("/sem_mutex"); // 删除互斥信号量
shmdt(buffer); // 将共享内存从进程地址空间中分离
shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
return 0;
}
```
在上面的代码中,我们创建了三个 System V 信号量,和一个共享内存:
- `sem_empty`:空闲空间信号量,初始值为缓冲池大小 N。
- `sem_full`:已用空间信号量,初始值为 0。
- `sem_mutex`:互斥信号量,用于对缓冲池的访问进行加锁,初始值为 1。
- `buffer`:缓冲池,使用共享内存实现。
在生产者进程中,我们使用 `sem_wait()` 函数等待空闲空间信号量,表示缓冲池中有空闲空间可以存放消息。然后使用 `sem_wait()` 函数对互斥信号量进行加锁,保证生产者进程与其他进程的访问是互斥的。接着,将消息存入缓冲池,并将消息数量加一。最后使用 `sem_post()` 函数释放互斥信号量和发送已用空间信号量,表示缓冲池中已经使用了一个空间。
在消费者进程中,我们使用 `sem_wait()` 函数等待已用空间信号量,表示缓冲池中有消息可以被消费者取出。然后使用 `sem_wait()` 函数对互斥信号量进行加锁,保证消费者进程与其他进程的访问是互斥的。接着,从缓冲池中取出消息,并将消息数量减一。最后使用 `sem_post()` 函数释放互斥信号量和发送空闲空间信号量,表示缓冲池中又有一个空间可以被生产者使用。
在主函数中,我们先创建共享内存和信号量,然后创建生产者和消费者进程,并使用 `waitpid()` 函数等待进程结束。最后删除信号量和共享内存并返回。
阅读全文