3 POSIX 多任务及同步机制-拓展实验 条件变量与生产者-消费者问题

夙愿已清 提交于 2020-02-02 01:11:46

3 POSIX 多任务及同步机制-拓展实验 条件变量与生产者-消费者问题

一.实验目的

·理解进程、线程同步问题。
·掌握POSIX条件变量机制的使用方法。
·深入理解在动态并发环境下,进程、线程在运行过程中的资源竞争应发的问题,如虚假唤醒

二.实验背景

·Recall: 进程的同步与互斥
同步问题
互斥问题
·互斥:一组并发进程中的一个或多个程序段,因共享某一公有资源而导致它们必须以一个不允许交叉执行的单位执行。
·同步(狭义):异步环境下的一组并发进程,因直接制约而互相发送消息而进行相互合作、互相等待,使得各进程按一定的速度执行的过程称为进程间的同步
·要解决线程之间的狭义同步问题,有如下两种思路。
·一种是使用轮询方法,也就是俗称的“忙等待”。就是等待条件满足的线程不断的去查询条件是否得到满足;这种方法实现较为简单,但是会有一定的性能消耗。如果轮询的间隔时间太短,由于上下文的切换就会消耗较多资源;而间隔时间太长则不能及时地响应。
·另外一种方法就是当条件不满足时,等待该条件的线程就会休眠;当条件满足时系统会唤醒等待该条件的线程,也被称为消息通知机制。
·条件变量的声明、初始化和销毁
声明:以变量方式声明
P.203, 初始化和销毁函数

·条件变量的使用:
等待:与一个互斥锁结合使用, pthread_cond_wait
唤醒: pthread_cond_signal

三.关键代码及分析

#define BUFFER_SIZE 16 // 缓冲区数量
#define PRO_NO 30 // PRODUCING NO
#define OVER ( - 1) //生产结束标志
#define PSLEEP 10000  // 生产者随机睡眠时间
#define CSLEEP 10000  // 消费者随机睡眠时间
#define PPNO 2  // 生产者数量
#define CPNO 2  // 消费者数量
pthread_mutex_t lock; /* 互斥体lock 用于对缓冲区的互斥操作 */
pthread_cond_t notempty; /* 缓冲区非空的条件变量 */
pthread_cond_t notfull; /* 缓冲区未满的条件变量 */

struct prodcons
{// 缓冲区相关数据结构
	int buf[BUFFER_SIZE]; /* 实际数据存放的数组*/
	int readpos, writepos; /* 读写指针*/
};

·条件变量的初始化(成功返回0,否则返回其他)

pthread_cond_init(&notempty, NULL);
pthread_cond_init(&notfull, NULL);

/* 创建生产者和消费者线程*/
	pthread_create(&th_c, NULL, producer, 0);
	pthread_create(&th_p, NULL, consumer, 0);
/* 等待两个线程结束*/
	pthread_join(th_c, &retval);
	pthread_join(th_p, &retval);
·条件变量的销毁(成功返回0,否则返回其他)
	pthread_cond_destroy(&notempty);
	pthread_cond_destroy(&notfull);

/* 等待缓冲区未满,应该用while判断,因为有可能发送虚假唤醒:期待的条件尚不成立的唤醒。*/
		while ((buffer.writepos + 1) % BUFFER_SIZE == buffer.readpos)
		//if ((buffer.writepos + 1) % BUFFER_SIZE == buffer.readpos) //会产生虚假唤醒
		{	
			pthread_cond_wait(&notfull, &lock);//实现条件等待
		}
//激活一个等待该条件的线程,如果该条件变量存在多个等待线程时,按入队顺序激活其中一个。
pthread_cond_signal(&notfull);
pthread_cond_signal(&notempty);

生产者:

void *producer(void *data)
{
	int n;
	for (n = 0; n <= PRO_NO; n++)
	{	
		pthread_mutex_lock(&lock);
		/* 等待缓冲区未满,应该用while判断,因为有可能发送虚假唤醒:期待的条件尚不成立的唤醒。*/
		while ((buffer.writepos + 1) % BUFFER_SIZE == buffer.readpos)
		//if ((buffer.writepos + 1) % BUFFER_SIZE == buffer.readpos) //会产生虚假唤醒
		{	
			pthread_cond_wait(&notfull, &lock);
		}
		/* 写数据,并移动指针 */
		if (n < PRO_NO)
		{
			buffer.buf[buffer.writepos] = n;
			printf("%d --->\n", n);
			usleep(PSLEEP);
		}
		else
		{
			buffer.buf[buffer.writepos] = OVER;			
			printf("%d --->\n", OVER);
		}
		buffer.writepos++;
		if (buffer.writepos >= BUFFER_SIZE)
			buffer.writepos = 0;
		/* 设置缓冲区非空的条件变量*/
		pthread_cond_signal(&notempty);
		pthread_mutex_unlock(&lock);
	} 
	return NULL;
}

消费者:

void *consumer(void *data)
{
	int d;
	while (1)
	{	
		pthread_mutex_lock(&lock);
		/* 等待缓冲区非空,应该用while判断,因为有可能发送虚假唤醒:期待的条件尚不成立的唤醒。*/
		while(buffer.writepos == buffer.readpos)
		//if(buffer.writepos == buffer.readpos) //会产生虚假唤醒
		{
			pthread_cond_wait(&notempty, &lock);
		}
		/* 读数据,移动读指针*/
		d = buffer.buf[buffer.readpos];
		//usleep(CSLEEP);
		buffer.readpos++;
		if (buffer.readpos >= BUFFER_SIZE)
			buffer.readpos = 0;
		/* 设置缓冲区未满的条件变量*/
		pthread_cond_signal(&notfull);
		pthread_mutex_unlock(&lock);
		printf("--->%d \n", d);
		if (d == OVER)
			break;
		
	}
	return NULL;
}

四.实验结果与分析

· gcc -o pro_csm pro_csm.c -lpthread
在这里插入图片描述
· ./pro_csm在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#define BUFFER_SIZE 16 // 缓冲区数量
#define PRO_NO 30 // PRODUCING NO
#define OVER ( - 1) //生产结束标志

由实验结果可以看出如果消费者消费时buffer为空则会进入等待状态,生产者生产时 buffer 满时则会进入等待状态。由结果可以看出当生产者开始生产后,发送一个信号激活消费者,并且释放锁,中间消费者和生产则交替的执行,当个生产者生产达到设置的最大值时,生产者线程结束,然后消费者继续消费直到消费完 buffer。

·将pthread_cond_write函数调用前的while 改为 if ,产生虚假唤醒
在这里插入图片描述

习题:

在这里插入图片描述

14-1:

在这里插入图片描述
在这里插入图片描述
新创建了7个进程,一共有1+7个进程
在这里插入图片描述

14-2

线程的分离状态detachstate;线程的调度策略:schedpolicy;线程的调度参数:schedparam;线程的继承性:inheritsched;线程的作用域:scope;

14-3

共享资源可同时被多个线程所使用,每个线程在使用共享资源时未意识到其他线程可能对共享资源进行的修改,造成数据的相互覆盖,形成资源的竞争。

14-4:

对某一代码段前后进行加锁,解锁,两者之间是临界区,在一个时间点上只允许一个线程进入临界区。其他线程进入等待队列。

14-5:

当生产者的(buffer.writepos + 1) % BUFFER_SIZE == buffer.readpos时,进入pthread_cond_wait(&notfull),进行阻塞,等待消费者的signal的notfull信号;
当消费者的buffer.writepos == buffer.readpos时,进入pthread_cond_wait(&notempty), 进行阻塞,等待生产者的signal的notempty信号;
形成只有生产者生产产品后消费者才能消费的同步和只有消费者消费后,生产者才能生产的同步。

练习:

在这里插入图片描述
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!