一、实验目的及要求
“生产者消费者”问题是一个著名的同时性编程问题的集合。通过编写经典的”生产者消费者”问题的实验,读者可以进一步熟悉 Linux 中多线程编程,并且掌握用信号量处理线程间的同步互斥问题。
二、实验仪器设备与软件环境
VMware Workstation Pro
三、实验过程及实验结果分析
“生产者消费者”问题描述如下。 有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲区中 拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时也必须等待。 它们之间的关系如下图所示:
这里要求用有名管道来模拟有限缓冲区,用信号量来解决生产者消费者问题中的同步和互斥问题。
(1) 使用信号量解决
实验结果:
实验代码:
#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)思考使用条件变量解决
实验结果:
实验代码:
#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;
}
特别注意:如果运行时出现这种情况:
说明此时你并没有权限,上个权限就好了(sudo -s)