这里贴出一个非常经典的关于多线程条件变量互斥锁的案例,即生产消费者模型。
我想说的是这里的while循环判断pthread_cond_wait,为何不能使用if的问题。
根据man查询到pthread_cond_signal这个函数至少会唤起1个线程,也就是说也有可能会唤起两个及以上个线程。因此,如果这里使用if而不是while,那么当被唤起两个线程时(考虑争夺仅有的一个资源时的情况),会发生第二个线程没有资源可以消费的情况,即发生head=NULL,后续操作出现断错误!因此,这里一定要使用while做一步循环判断,这样即使两个线程都被唤起,那么第二个线程会再次判断head是否为NULL,如果是NULL,就再次wait。
代码如下,是他人写的,但是我主要点出while(if)那里:
/*借助条件变量模拟,生产者-消费者问题*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
/*链表作为共享数据,需被互斥量保护*/
struct msg {
struct msg *next;
int num;
};
struct msg *head;
struct msg *mp;
/*静态初始化一个全局条件变量和一个全局互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *consumer(void *p)
{
for(;;){
pthread_mutex_lock(&lock);
if(head == NULL){ //头指针为空,说明没有节点。使用while而不使用if。
pthread_cond_wait(&has_product, &lock);
//注意,在wait阻塞期间,该线程会释放之前lock的锁,因此生产者是可以加锁的。每当有signal或者broadcast has_product时,该函数便返回,while再次判断head是否为NULL,条件为否,因此可以向下执行。如果这里是if而不是while的话,那么直接往下执行。而signal的发送是保证至少有一个线程是捕获到,因此如果有两个线程同时捕捉到,并且wait返回时,虽然一个线程是会锁定互斥变量,成功操作,但是另一个线程由于没有再次判断head是否为NULL,因此等他获得锁再操作时,其实head已经为NULL了(上一个线程消费了唯一的一个产品),因此发生段错误。因此这里必须要使用while,从而可以再次wait检查锁占用情况。
}
mp = head;
head = mp->next; //模拟消费掉一个产品
printf("-Consume ---%d\n", mp->num);
free(mp);
mp = NULL;
pthread_mutex_unlock(&lock);
//sleep(rand() % 5);
}
}
void *producer(void *p)
{
for(;;){
pthread_mutex_lock(&lock);//必须在mp之前,防止mp刚malloc出来就又被新malloc出来的替代。
mp = malloc(sizeof(struct msg));
//模拟生产一个产品
mp->num = rand() % 1000 + 1;
printf("-Produce ---%d\n", mp->num);
mp->next = head;
head = mp;
pthread_mutex_unlock(&lock);
//将等待在该条件变量上的一个线程唤醒
pthread_cond_signal(&has_product);
//sleep(rand() % 5);
}
}
int main(int argc, char * argv)
{
pthread_t pid, cid;
srand(time(NULL));
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
错误如下:
因篇幅问题不能全部显示,请点此查看更多更全内容