实验目的
“生产者消费者”问题是一个著名的同时性编程问题的集合。通过编写经典的“生产者消费者”问题的实验,读者可以进一步熟悉Linux 中多线程编程,并且掌握用信号量处理线程间的同步互斥问题。
实验内容
“生产者消费者”问题描述如下。
有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲区中拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时也必须等待。它们之间的关系如下图所示:
这里要求用有名管道来模拟有限缓冲区,用信号量来解决生产者消费者问题中的同步和互斥问题。
实验步骤
(1)使用信号量解决
(2)思考使用条件变量解决
实验过程
(1信号量的考虑
这里使用3个信号量,其中两个信号量avail和full分别用于解决生产者和消费者线程之间的同步问题,mutex是用于这两个线程之间的互斥问题。其中avail初始化为N(有界缓冲区的空单元数),mutex 初始化为1,full初始化为0。
/*product.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>
#include <fcntl.h>
#define FIFO "myfifo"
#define N 5
int lock_var;
time_t end_time;
char buf_r[100];
sem_t mutex,full,avail;
int fd;
void pthread1(void *arg);
void pthread2(void *arg);
int main(int argc, char *argv[])
{
pthread_t id1,id2;
pthread_t mon_th_id;
int ret;
end_time = time(NULL)+30;
/*创建有名管道*/
if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf("cannot create fifoserver\n");
printf("Preparing for reading bytes...\n");
memset(buf_r,0,sizeof(buf_r));
/*打开管道*/
fd=open(FIFO,O_RDWR|O_NONBLOCK,0);
if(fd==-1)
{
perror("open");
exit(1);
}
/*初始化互斥信号量为1*/
ret=sem_init(&mutex,0,1);
/*初始化avail信号量为N*/
ret=sem_init(&avail,0,N);
/*初始化full信号量为0*/
ret=sem_init(&full,0,0);
if(ret!=0)
{
perror("sem_init");
}
/*创建两个线程*/
ret=pthread_create(&id1,NULL,(void *)productor, NULL);
if(ret!=0)
perror("pthread cread1");
ret=pthread_create(&id2,NULL,(void *)consumer, NULL);
(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;
}
实验结果
六 总结
与fork不同的是pthread_ create创建的线程不与父线程在同一点开始运行,而是从指定的函.数开始运行, 该函数运行完后,该线程也就退了。
线程依赖进程存在的,如果创建线程的进程结束了,线程也就结了。.
线程函数的程序在pthread库中,故链接时要加上参数lpthread.
gcc pthread_ _test.c -0 pthread test -Ipthread
掌握线程的基本操作,了解函数的基本操作。