在虚拟机中完成PV操作验证实验
本实验通过PV操作实现经典的生产者-消费者问题变体。实验设计了一个盘子作为共享资源(容量为1),父亲和母亲作为生产者分别放入苹果和橘子,女儿和儿子作为消费者分别取用对应水果。使用信号量empty控制盘子空间,mutex实现互斥访问,apple/orange信号量协调生产者消费者。实验在Linux环境下用多线程编程实现,通过sem_wait/sem_post模拟PV操作。后续改进将盘子容量扩展为3,
一、实验目的与要求
(1)实验环境:Vmware player虚拟机
(2)实验要求:完成书上例子:桌子上有一只盘子,最多可容纳一个水果,每次只能放入或取出一个水果。爸爸专向盘子放苹果(apple),妈妈专向盘子中放桔子(orange);儿子专等吃盘子中的桔子,女儿专等吃盘子中的苹果。请用P、V操作来实现爸爸、妈妈、儿子、女儿之间的同步与互斥关系。
(3)实验目的:在linux中学习使用多线程来实现PV操作。
二、实验内容及分析
- 实验题目
桌子上有一只盘子,最多可容纳一个水果,每次只能放入或取出一个水果。爸爸专向盘子放苹果(apple),妈妈专向盘子中放桔子(orange);儿子专等吃盘子中的桔子,女儿专等吃盘子中的苹果。请用P、V操作来实现爸爸、妈妈、儿子、女儿之间的同步与互斥关系。
2. 实验设计与分析
(1)实验过程
定义信号量:
我们需要定义四个信号量,分别表示盘子中的苹果、盘子中的桔子、盘子的互斥访问,以及表示盘子是否为空。初始化时,苹果和桔子信号量的初始值都为0,互斥信号量的初始值为1,空盘子信号量的初始值为1。
爸爸操作:
爸爸通过P操作等待空盘子信号量,确保盘子不为空,然后通过P操作等待互斥信号量,保证爸爸独占盘子。接着,爸爸向盘子中放入苹果,通过V操作发出盘子中苹果的信号,然后通过V操作释放互斥信号,表示爸爸放完苹果了。
妈妈操作:
妈妈操作与爸爸操作类似,通过P操作等待空盘子信号量,通过P操作等待互斥信号量,然后向盘子中放入桔子,通过V操作发出盘子中桔子的信号,最后通过V操作释放互斥信号。
儿子操作:
儿子通过P操作等待盘子中桔子的信号,确保盘子中有桔子。接着,通过P操作等待互斥信号,保证儿子独占盘子,然后儿子从盘子中取出桔子,通过V操作发出空盘子的信号,最后通过V操作释放互斥信号。
女儿操作:
女儿操作与儿子操作类似,通过P操作等待盘子中苹果的信号,确保盘子中有苹果。然后,通过P操作等待互斥信号,保证女儿独占盘子,女儿从盘子中取出苹果,通过V操作发出空盘子的信号,最后通过V操作释放互斥信号。
(2)PV操作代码
semaphore empty=1,mutex=1,apple=0,orange=0;
void father(){
do{
P(empty); //等待盘子为空
P(metux); //等待获取对盘子的操作
爸爸向盘中放一个苹果;
V(mutex); //释放对盘子的操作
V(apple); //通知女儿可以来盘子中取苹果
}while(TRUE);}
void mather(){
do{
P(empty); //等待盘子为空
P(metux); //等待获取对盘子的操作
妈妈向盘中放一个桔子;
V(mutex); //释放对盘子的操作
V(orange); //通知儿子可以来盘子中取橘子
}while(TRUE);}
void son(){
do{
P(orange); //判断盘子中是否有桔子
P(metux); //等待获取对盘子的操作
儿子取出盘中的桔子;
V(mutex); //释放对盘子的操作
V(empty); //盘子空了,可以继续放水果了
}while(TRUE);}
void daugther(){
do{
P(apple); //判断盘子中是否有苹果
P(metux); //等待获取对盘子的操作
女儿1取出盘中的苹果;
V(mutex); //释放对盘子的操作
V(empty); //盘子空了,可以继续放水果了
}while(TRUE);}
void main() { //四个并发进程的同步执行
cobegin
father(); mather(); son();son();daugther();daugther();
coend
}
3. 实验代码
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#define total 1
sem_t empty, apple, orange;
static unsigned int vempty = 1, vapple = 0, vorange = 0;
void *father(void *arg) {
while(1) {
sem_wait(&empty);
vempty--;
vapple++;
printf("父亲放苹果, 剩余空间=%u, 苹果数=%u\n", vempty, vapple);
sem_post(&apple);
sleep(1);}}
void *mather(void *arg) {
while(1) {
sem_wait(&empty);
vempty--;
vorange++;
printf("母亲放橘子, 剩余空间=%u, 橘子数=%u\n", vempty, vorange);
sem_post(&orange);
sleep(2);}}
void *son(void *arg) {
while(1) {
sem_wait(&orange);
vorange--;
vempty++;
printf("儿子吃橘子, 剩余空间=%u, 橘子数=%u\n", vempty, vorange);
sem_post(&empty);
sleep(2);}}
void *daughter(void *arg) {
while(1) {
sem_wait(&apple);
vapple--;
vempty++;
printf("女儿吃苹果, 剩余空间=%u, 苹果数=%u\n", vempty, vapple);
sem_post(&empty);
sleep(1);}}
int main() {
pthread_t fa, ma, so ,da;
sem_init(&empty, 0, total);//总数初始化为1
sem_init(&apple, 0, 0);//盆子中苹果数, 开始为0
sem_init(&orange, 0, 0);//盆子中梨子数, 开始为0
pthread_create(&fa, NULL, &father, NULL);
pthread_create(&ma, NULL, &mather, NULL);
pthread_create(&so, NULL, &son, NULL);
pthread_create(&da, NULL, &daughter, NULL);
for(;;);}
4. 实验结果展示
5. 实验题目改进
桌子上有一只盘子,最多可容纳三个水果,每次只能放入或取出一个水果。爸爸专向盘子放苹果(apple),妈妈专向盘子中放桔子(orange);儿子和小猫专等吃盘子中的桔子,女儿和小狗专等吃盘子中的苹果。请用P、V操作来实现爸爸、妈妈、儿子、女儿之间的同步与互斥关系。
这里会有六个进程,包括爸爸、妈妈、儿子、女生、小狗、小猫。
盘子的剩余空间为3。
6. 改进代码
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#define total 3
sem_t remain, apple, orange, mutex;
static unsigned int vremain=3, vapple = 0, vorange = 0;
void *father(void *arg) {
while(1) {
sem_wait(&remain);
sem_wait(&mutex);
vremain--;
vapple++;
printf("父亲放苹果, 剩余空间=%u, 苹果数=%u\n", vremain, vapple);
sem_post(&mutex);
sem_post(&apple);
sleep(1);}}
void *mather(void *arg) {
while(1) {
sem_wait(&remain);
sem_wait(&mutex);
vremain--; vorange++;
printf("母亲放橘子, 剩余空间=%u, 橘子数=%u\n", vremain, vorange);
sem_post(&mutex);
sem_post(&orange);
sleep(2);}}
void *son1(void *arg) {
while(1) {
sem_wait(&orange);
sem_wait(&mutex);
vremain++; vorange--;
printf("儿子吃橘子, 剩余空间=%u, 橘子数=%u\n", vremain, vorange);
sem_post(&mutex);
sem_post(&remain);
sleep(3);}}
void *cat(void *arg) {
while(1) {
sem_wait(&orange);
sem_wait(&mutex);
vremain++; vorange--;
printf("小猫吃橘子, 剩余空间=%u, 橘子数=%u\n", vremain, vorange);
sem_post(&mutex);
sem_post(&remain);
sleep(8);}}
void *daughter1(void *arg) {
while(1) {
sem_wait(&apple);
sem_wait(&mutex);
vremain++; vapple--;
printf("女儿吃苹果, 剩余空间=%u, 苹果数=%u\n", vremain, vapple);
sem_post(&mutex);
sem_post(&remain);
sleep(3);}}
void *dog(void *arg) {
while(1) {
sem_wait(&apple);
sem_wait(&mutex);
vremain++; vapple--;
printf("小狗吃苹果, 剩余空间=%u, 苹果数=%u\n", vremain, vapple);
sem_post(&mutex);
sem_post(&remain);
sleep(8);}}
int main() {
pthread_t fa, ma, so, da;
sem_init(&remain, 0, total);//总数初始化为2
sem_init(&apple, 0, 0);//盆子中苹果数, 开始为0
sem_init(&orange, 0, 0);//盆子中梨子数, 开始为0
sem_init(&mutex, 0, 1);//互斥锁, 初始为1
pthread_create(&fa, NULL, &father, NULL);
pthread_create(&ma, NULL, &mather, NULL);
pthread_create(&so, NULL, &son1, NULL);
pthread_create(&da, NULL, &daughter1, NULL);
pthread_create(&da, NULL, &dog, NULL);
pthread_create(&so, NULL, &cat, NULL);
for(;;);}
7. 改进结果展示
三、小总结与进一步改进设想
通过实现基于信号量的同步和互斥操作的代码,我不仅学到了信号量的概念:,了解了信号量作为同步和互斥的机制,通过 sem_wait 和 sem_post 操作来进行等待和发信号。通过父亲母亲儿子女儿的同步和互斥的应用,我学会了如何使用信号量来实现多线程之间的同步和互斥,确保线程之间的合作和互斥操作。并熟悉了多线程编程的基本概念,包括创建线程、线程执行函数、线程同步等。
此外,我通过改进原始代码,增加了家庭成员和动物的数量,扩展了程序的复杂性和趣味性。同时,通过增加可配置参数、异常处理、日志记录等,学会了如何设计更健壮、灵活和可维护的程序。
了解了如何在多线程环境下,通过合适的同步机制,解决共享资源的并发控制问题,防止竞争条件的发生。
一些改进:
(1)可配置参数:在改进后的代码中,可以考虑将一些常量参数,例如总数 total,以及 sleep 函数中的延迟时间,变为可配置的参数,使得用户可以更方便地调整程序的行为。
(2)异常处理:在使用信号量和线程创建时,可以添加一些异常处理机制,捕获可能发生的错误,提高程序的稳定性。
(3)交互式界面:考虑使用更友好的交互方式,例如通过命令行参数或图形用户界面(GUI)来输入一些参数,使得程序更易用。
(4)日志记录:添加日志记录功能,将各个家庭成员和动物的动作记录到日志文件中,方便调试和观察程序的执行过程。
更多推荐
所有评论(0)