一、实验目的及要求

“生产者消费者”问题是一个著名的同时性编程问题的集合。通过编写经典的”生产者消费者”问题的实验,读者可以进一步熟悉 Linux 中多线程编程,并且掌握用信号量处理线程间的同步互斥问题。

二、实验仪器设备与软件环境

VMware Workstation Pro

三、实验过程及实验结果分析

“生产者消费者”问题描述如下。 有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲区中 拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时也必须等待。 它们之间的关系如下图所示:

zookeeper生产者消费者 linux生产者消费者实验_#include

这里要求用有名管道来模拟有限缓冲区,用信号量来解决生产者消费者问题中的同步和互斥问题。

(1) 使用信号量解决

实验结果:

zookeeper生产者消费者 linux生产者消费者实验_#include_02


实验代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<pthread.h>
#include<semaphore.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/stat.h>
#define MYFIFO        "myfifo"    /*缓冲区有名管道的名字*/
#define BUFFER_SIZE        3        /*缓冲区的单元数*/
#define UNIT_SIZE    6            /*每个单元的字节大小*/
#define RUN_TIME    30            /*运行时间*/
#define DELAY_TIME_LEVELS    5.0    /*周期的最大值*/

int fd;  /*管道描述符*/
time_t end_time;  /*存放线程的起始时间*/
sem_t mutex,full,avail;  /*信号量描述符*/

/*生产者线程*/
void *producer(void *arg)
{
    int real_write;  /*实际写入字节数*/
    int delay_time=0;

    /*time(NULL)返回从公元1970年1月1日的UTC时间0时0分0秒算起
 到现在所经过的秒数,while()的意思就是说如果在执行生产者线程的
 那一刻没有超过其结束时间,那么则执行*/
    while(time(NULL)<end_time)
    {
        delay_time=(int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX)/2.0)+1;
        sleep(delay_time);
        /*P操作信号量 avail 和 mutex */   
        sem_wait(&avail);
        sem_wait(&mutex);
        printf("\nProducer:delay=%d\n",delay_time);
        /*生产者写入数据*/
        if((real_write=write(fd,"hello",UNIT_SIZE))==-1)
        {  
            /*这个errno=EAGAIN表示的是你的write本来是非阻塞情况,现在没有数
 据可读,这个时候就会置全局变量errno为EAGINA,表示可以再次进行读
 操作;如果是阻塞情况,那么被中断的话,errno=EINTR*/
            if(errno==EAGAIN)
            {
                printf("The FIFO has not been read yet.Please try later\n");
            }
        }
        else
        {
            printf("Write %d to the FIFO\n",real_write);
        }
        /*V操作信号量full和mutex*/
        sem_post(&full);
        sem_post(&mutex);
    }
    pthread_exit(NULL);
}

/*消费者线程*/
void *customer(void *arg)
{
    unsigned char read_buffer[UNIT_SIZE];
    int real_read;
    int delay_time;

    while(time(NULL)<end_time)
    {
        delay_time=(int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX))+1;
        sleep(delay_time);
        /*P操作信号量full和mutex*/
        sem_wait(&full);
        sem_wait(&mutex);
        memset(read_buffer,0,UNIT_SIZE);
        printf("\nCustomer:delay=%d\n",delay_time);
    
        if((real_read=read(fd,read_buffer,UNIT_SIZE))==-1)
        {
            if(errno==EAGAIN)
            {
                printf("No data yet\n");
            }
        }
        printf("Read %s from FIFO\n",read_buffer);
        /*V操作信号量avail和mutex*/
        sem_post(&avail);
        sem_post(&mutex);
    }
    pthread_exit(NULL);
}

//主函数

int main()
{
    pthread_t thrd_pro_id,thrd_cus_id;
    pthread_t mon_th_id;
    int ret;

    srand(time(NULL));/*随机数发生器初始化*/
    end_time=time(NULL)+RUN_TIME;
    /*创建有名管道*/
    if((mkfifo(MYFIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
    {
        printf("Cannot create fifo\n");
        return errno;
    }
    /*打开管道*/
    fd=open(MYFIFO,O_RDWR,0666);
    if(fd==-1)
    {
        printf("Open fifo error\n");
        return fd;
    }
    
    /*初始化互斥信号量为1*/
    ret=sem_init(&mutex,0,1);
    /*初始化avail信号量为N*/
    ret+=sem_init(&avail,0,BUFFER_SIZE);
    /*初始化full信号量为0*/
    ret+=sem_init(&full,0,0);
    if(ret!=0)
    {
        printf("Any semaphore initialization failed\n");
        return ret;
    }
    
    /*创建两个进程*/
    ret=pthread_create(&thrd_pro_id,NULL,producer,NULL);
    if(ret!=0)
    {
        printf("Create producer thread error\n");
        return ret;
    }
    ret=pthread_create(&thrd_cus_id,NULL,customer,NULL);
    if(ret!=0)
    {
        printf("Create producer thread error\n");
        return ret;
    }
    pthread_join(thrd_pro_id,NULL);
    pthread_join(thrd_cus_id,NULL);
    close(fd);
//    unlink(MYFIFO);  /*所有打开该文件的进程都结束时文件被删除*/
    return 0;
}

(2)思考使用条件变量解决

实验结果:

zookeeper生产者消费者 linux生产者消费者实验_服务器_03


zookeeper生产者消费者 linux生产者消费者实验_#include_04


实验代码:

#include <stdio.h>
 #include <pthread.h>
 #define BUFFER_SIZE 4
 #define OVER (-1)
 struct producers//定义生产者条件变量结构
 {
 	int buffer[BUFFER_SIZE];
	pthread_mutex_t lock;
 	int readpos, writepos;
 	pthread_cond_t notempty;
 	pthread_cond_t notfull;
 };
 //初始化缓冲区
 void init(struct producers *b)
 {
 	pthread_mutex_init(&b->lock,NULL);
 	pthread_cond_init(&b->notempty,NULL);
 	pthread_cond_init(&b->notfull,NULL);
 	b->readpos=0;
 	b->writepos=0;
 }
 //在缓冲区存放一个整数
 void put(struct producers *b, int data)
 {
 	pthread_mutex_lock(&b->lock);
 //当缓冲区为满时等待
 	while((b->writepos+1)%BUFFER_SIZE==b->readpos)
 	{
 		pthread_cond_wait(&b->notfull,&b->lock);
 	}
 	b->buffer[b->writepos]=data;
 	b->writepos++;
 	if(b->writepos>=BUFFER_SIZE) b->writepos=0;
 	//发送当前缓冲区中有数据的信号
 	pthread_cond_signal(&b->notempty);
 	pthread_mutex_unlock(&b->lock);
 }
 
 int get(struct producers *b)
 {
 	int data;
 	pthread_mutex_lock(&b->lock);
 	while(b->writepos==b->readpos)
 	{
 	pthread_cond_wait(&b->notempty,&b->lock);
 	}
 	data=b->buffer[b->readpos];
 	b->readpos++;
 	if(b->readpos>=BUFFER_SIZE) b->readpos=0;
 	pthread_cond_signal(&b->notfull);
 	pthread_mutex_unlock(&b->lock);
 	return data;
 }
 struct producers buffer;
 void *producer(void *data)
 {
 	int n;
 	for(n=0;n<10;n++)
 	{
 	printf("Producer : %d-->\n",n);
 	put(&buffer,n);
 	}
 	put(&buffer,OVER);
 	return NULL;
 }
 
 void *consumer(void *data)
 {
 	int d;
 	while(1)
 	{
 	d=get(&buffer);
 	if(d==OVER) break;
 	printf("Consumer: --> %d\n",d);
 	}
 return NULL;
 }

 int main()
 {
 	pthread_t tha,thb;
 	void *retval;
 	init(&buffer);
 	pthread_create(&tha,NULL,producer,0);
 	pthread_create(&thb,NULL,consumer,0);
 	pthread_join(tha,&retval);
 	pthread_join(thb,&retval);
 	return 0;
 }

特别注意:如果运行时出现这种情况:

zookeeper生产者消费者 linux生产者消费者实验_运维_05


说明此时你并没有权限,上个权限就好了(sudo -s)

zookeeper生产者消费者 linux生产者消费者实验_#include_06