一、实验目的与要求

(1)实验环境:Vmware player虚拟机

(2)实验要求:完成书上例子:桌子上有一只盘子,最多可容纳一个水果,每次只能放入或取出一个水果。爸爸专向盘子放苹果(apple),妈妈专向盘子中放桔子(orange);儿子专等吃盘子中的桔子,女儿专等吃盘子中的苹果。请用P、V操作来实现爸爸、妈妈、儿子、女儿之间的同步与互斥关系。

(3)实验目的:在linux中学习使用多线程来实现PV操作。

二、实验内容及分析

  1. 实验题目

桌子上有一只盘子,最多可容纳一个水果,每次只能放入或取出一个水果。爸爸专向盘子放苹果(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)日志记录:添加日志记录功能,将各个家庭成员和动物的动作记录到日志文件中,方便调试和观察程序的执行过程。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐