3 POSIX 多任务及同步机制-拓展实验 条件变量与生产者-消费者问题
一.实验目的
·理解进程、线程同步问题。
·掌握POSIX条件变量机制的使用方法。
·深入理解在动态并发环境下,进程、线程在运行过程中的资源竞争应发的问题,如虚假唤醒
二.实验背景
·Recall: 进程的同步与互斥
同步问题
互斥问题
·互斥:一组并发进程中的一个或多个程序段,因共享某一公有资源而导致它们必须以一个不允许交叉执行的单位执行。
·同步(狭义):异步环境下的一组并发进程,因直接制约而互相发送消息而进行相互合作、互相等待,使得各进程按一定的速度执行的过程称为进程间的同步
·要解决线程之间的狭义同步问题,有如下两种思路。
·一种是使用轮询方法,也就是俗称的“忙等待”。就是等待条件满足的线程不断的去查询条件是否得到满足;这种方法实现较为简单,但是会有一定的性能消耗。如果轮询的间隔时间太短,由于上下文的切换就会消耗较多资源;而间隔时间太长则不能及时地响应。
·另外一种方法就是当条件不满足时,等待该条件的线程就会休眠;当条件满足时系统会唤醒等待该条件的线程,也被称为消息通知机制。
·条件变量的声明、初始化和销毁
声明:以变量方式声明
P.203, 初始化和销毁函数
·条件变量的使用:
等待:与一个互斥锁结合使用, pthread_cond_wait
唤醒: pthread_cond_signal
三.关键代码及分析
#define BUFFER_SIZE 16
#define PRO_NO 30
#define OVER ( - 1)
#define PSLEEP 10000
#define CSLEEP 10000
#define PPNO 2
#define CPNO 2
pthread_mutex_t lock;
pthread_cond_t notempty;
pthread_cond_t notfull;
struct prodcons
{
int buf[BUFFER_SIZE];
int readpos, writepos;
};
·条件变量的初始化(成功返回0,否则返回其他)
pthread_cond_init(¬empty, NULL);
pthread_cond_init(¬full, 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(¬empty);
pthread_cond_destroy(¬full);
while ((buffer.writepos + 1) % BUFFER_SIZE == buffer.readpos)
{
pthread_cond_wait(¬full, &lock);
}
pthread_cond_signal(¬full);
pthread_cond_signal(¬empty);
生产者:
void *producer(void *data)
{
int n;
for (n = 0; n <= PRO_NO; n++)
{
pthread_mutex_lock(&lock);
while ((buffer.writepos + 1) % BUFFER_SIZE == buffer.readpos)
{
pthread_cond_wait(¬full, &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(¬empty);
pthread_mutex_unlock(&lock);
}
return NULL;
}
消费者:
void *consumer(void *data)
{
int d;
while (1)
{
pthread_mutex_lock(&lock);
while(buffer.writepos == buffer.readpos)
{
pthread_cond_wait(¬empty, &lock);
}
d = buffer.buf[buffer.readpos];
buffer.readpos++;
if (buffer.readpos >= BUFFER_SIZE)
buffer.readpos = 0;
pthread_cond_signal(¬full);
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
#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(¬full),进行阻塞,等待消费者的signal的notfull信号;
当消费者的buffer.writepos == buffer.readpos时,进入pthread_cond_wait(¬empty), 进行阻塞,等待生产者的signal的notempty信号;
形成只有生产者生产产品后消费者才能消费的同步和只有消费者消费后,生产者才能生产的同步。
练习:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)