简单介绍一下使用条件变量时,增加互斥锁的必要性。pthread_cond_wait的API如下
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
条件变量的用法就不具体介绍了,此处我们考虑的问题是增加mutex参数的必要性,先看看课本中的使用方法(只贴了主要的部分,包含生产者和消费者线程):
#include <stdio.h>
#include <pthread.h>
#define MAX 1000000000
pthread_mutex_t the_mutex;/*互斥锁*/
pthread_cond_t condc, condp;/*消费者和生产者条件变量*/
int buffer = 0;
void *producer(void *ptr)
{
int i;
for(i = 1; i <= MAX; i++)
{
pthread_mutex_lock(&the_mutex);
while(buffer != 0)/*1*/
{
pthread_cond_wait(&condp, &the_mutex);/*2*/
}
buffer = i;
pthread_cond_signal(&condc);
pthread_mutex_unlock(&the_mutex);
}
pthread_exit(0);
}
void *consumer(void *ptr)
{
int i;
for(i = 1; i <= MAX; i++)
{
pthread_mutex_lock(&the_mutex);
while(buffer == 0)/*3*/
{
pthread_cond_wait(&condc, &the_mutex);/*4*/
}
buffer = 0;
pthread_cond_signal(&condp);
pthread_mutex_unlock(&the_mutex);
}
pthread_exit(0);
}
线程在调用pthread_cond_wait并导致阻塞时会解锁互斥锁,解除阻塞并离开pthread_cond_wait时会加锁互斥锁。从代码中可以看到,该特性可以保证语句<1>和语句<3>中对buffer的访问和之后调用pthread_cond_wait导致的阻塞操作的原子性。
上面的解释可能不够直观,我们考虑一种错误的用法(去掉互斥锁),代码如下:
void *producer(void *ptr)
{
int i;
for(i = 1; i <= MAX; i++)
{
while(buffer != 0)/*1*/
{
pthread_cond_wait(&condp, NULL);/*2*/
}
buffer = i;
pthread_cond_signal(&condc);
}
pthread_exit(0);
}
void *consumer(void *ptr)
{
int i;
for(i = 1; i <= MAX; i++)
{
while(buffer == 0)/*3*/
{
pthread_cond_wait(&condc, NULL);/*4*/
}
buffer = 0;
pthread_cond_signal(&condp);
}
pthread_exit(0);
}
我们考虑一种可能的线程运行时序,如下所示(buffer初始值为0):
从图中可以看到在consumer线程对buffer进行访问并准备阻塞的时候,切换至producer线程运行,该线程更改了buffer的值,最终导致两个线程均被阻塞,发生死锁。
由此可以看出,在使用条件变量时,对需要判断的条件添加互斥锁的必要性。
网友评论