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

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

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

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

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

/*producer_customer.c*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#include <sys/ipc.h>

/*定义有名管道的文件名*/
#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;
}

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





1、实验目的 (1)掌握基本的同步互斥算法,理解生产者消费者同步的问题模型。 (2)了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 (3)学习使用Windows2000/XP中基本的同步对象,掌握相应的API。 2、实验要求 (1)创建生产者消费者线程 在Windows2000环境下,创建一个控制台进程,在此进程中创建n个线程来模拟生产者或者消费者。这些线程的信息由本程序定义的“测试用例文件”中予以指定。 该文件的格式和含义如下: 3 1 P 3 2 P 4 3 C 4 1 4 P 2 5 C 3 1 2 4 第一行说明程序中设置几个临界区,其余每行分别描述了一个生产者或者消费者线程的信息。每一行的各字段间用Tab键隔开。不管是消费者还是生产者,都有一个对应的线程号,即每一行开始字段那个整数。第二个字段用字母P或者C区分是生产者还是消费者。第三个字段表示在进入相应线程后,在进行生产和消费动作前的休眠时间,以秒计时;这样做的目的是可以通过调整这一列参数,控制开始进行生产和消费动作的时间。如果是代表生产者,则该行只有三个字段。如果代表消费者,则该行后边还有若干字段,代表要求消费的产品所对应的生产者线程号。所以务必确认这些对应的线程号存在并且该线程代表一个生产者。 (2)生产和消费的规则 在按照上述要求创建线程进行相应的读写操作时,还需要符合以下要求: ①共享缓冲区存在空闲空间时,生产者即可使用共享缓冲区。 ②从上边的测试数据文件例子可以看出,某一生产者生产一个产品后,可能不止一个消费者,或者一个消费者多次地请求消费该产品。此时,只有当所有的消费需求都被满足以后,该产品所在的共享缓冲区才可以被释放,并作为空闲空间允许新的生产者使用。 ③每个消费者线程的各个消费需求之间存在先后顺序。例如上述测试用例文件包含一行信息“5 C 3 l 2 4”,可知这代表一个消费者线程,该线程请求消费1,2,4号生产者线程生产的产品。而这种消费是有严格顺序的,消费1号线程产品的请求得到满足后才能继续往下请求2号生产者线程产品。 ④要求在每个线程发出读写操作申请、开始读写操作和结束读写操作时分别显示提示信息。 (3)相关基础知识 本实验所使用的生产者消费者模型具有如下特点: 本实验的多个缓冲区不是环形循环的,也不要求按顺序访问。生产者可以把产品放到目前某一个缓冲区中。 消费者只消费指定生产者产品。 在测试用例文件中指定了所有的生产和消费的需求,只有当共享缓冲区的数据满足了所有关于它的消费需求后,此共享缓冲区才可以作为空闲空间允许新的生产者使用。 本实验在为生产者分配缓冲区时各生产者间必须互斥,此后各个生产者的具体生产活动可以并发。而消费者之间只有在对同一产品进行消费时才需要互斥,同时它们在消费过程结束时需要判断该消费对象是否已经消费完毕并清除该产品。 Windows用来实现同步和互斥的实体。在Windows中,常见的同步对象有:信号量(Semaphore)、互斥量(Mutex)、临界段(CriticalSection)等。使用这些对象都分为三个步骤,一是创建或者初始化:接着请求该同步对象,随即进入临界区,这一步对应于互斥量的上锁;最后释放该同步对象,这对应于互斥量的解锁。这些同步对象在一个线程中创建,在其他线程中都可以使用,从而实现同步互斥。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值