线程实现生产者消费者实验

生产者消费者问题描述如下:

有一个有限缓冲区和两个线程,生产者和消费者,他们分别不停地把产品放到缓冲区和从缓冲区拿走数据,一个生产者在缓冲区满的时候必须等待,而消费者在缓冲区空时也必须等待,又因为缓冲区时临界资源,故要实现两个线程之间的互斥访问。在本实验中采用管道来模拟有限缓冲区,并使用信号量来解决同步和互斥问题。

在这里我们使用三个信号量:mutex,avail,full。其中两个信号量avail和full分别用于解决同步问题,mutex解决互斥的问题。avail表示缓冲区中空单元数,初始值为N,full表示缓冲区中非空单元数,初始值为0,mutex为互斥信号量,初始值为1.

在本实验中,缓冲去为3个单元,每个单元5个字节。另外,使用随机时间延迟的方法实现两个线程的随机访问缓冲区,这更能体现信号量的价值。生产者每次放入一个hello字符串,消费者每次取走一个hello字符串。并且生产者的平均速度是消费者的2倍。源代码:

/*producer_customer.c*/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*定义有名管道的文件名*/
#define MYFIFO			"/tmp/myfifo"
/*缓冲区单元数*/
#define BUFFER_UNITS		3
/*每个缓冲区单元大小*/
#define UNIT_SIZE		5
/*程序运行时间*/
#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;

/*当结束时间点没有到来之前,一直循环*/
	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 )
		{
			if( errno == EAGAIN )
				printf("The FIFO has not been read yet,please try later!\n");
		}
		else
			printf("Write %d to 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);
		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");
		}
		else
			printf("Read %s from FIFO\n",read_buffer);
		sem_post(&avail);
		sem_post(&mutex);
	}
	
	pthread_exit(NULL);
}

int main(void)
{
	pthread_t thrd_prd_id,thrd_cst_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);
	if( fd == -1 )
	{
		printf("Open fifo error!\n");
		return fd;
	}
/*初始化信号量mutex,avail,full*/
	ret = sem_init(&mutex,0,1);
	ret += sem_init(&avail,0,BUFFER_UNITS);
	ret += sem_init(&full,0,0);
	if( ret != 0)
	{
		printf("Any semaphore initialization failed!\n");
		return ret;
	}
/*创建生产者线程*/
	ret = pthread_create(&thrd_prd_id,NULL,producer,NULL);
	if( ret != 0 )
	{
		printf("create producer thread error!\n");
		return ret;
	}
/*创建消费者线程*/
	ret = pthread_create(&thrd_cst_id,NULL,customer,NULL);
	if( ret != 0 )
	{
		printf("create customer thread error!\n");
		return ret;
	}
/*分别等待生产者和消费者线程结束*/
	pthread_join(thrd_prd_id,NULL);
	pthread_join(thrd_cst_id,NULL);
/*关闭管道文件描述符*/
	close(fd);
/*关闭管道文件链接,删除管道文件*/
	unlink(MYFIFO);
	
	return 0;
}

编译运行,可以看到运行结果。





你可能感兴趣的:(Linux学习历程)