多线程之条件变量pthread_cond_t、pthread_cond_wait使用
以下代码看起来没有什么问题,但是编译运行多次后,就有可能出现问题。
//使用条件变量实现生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
int data;
struct node *next;
}NODE;
NODE *head = NULL;
//定义一把锁
pthread_mutex_t mutex;
//定义条件变量
pthread_cond_t cond;
//生产者线程
void *producer(void *arg)
{
NODE *pNode = NULL;
int n = *(int *)arg;
while(1)
{
//生产一个节点
pNode = (NODE *)malloc(sizeof(NODE));
if(pNode==NULL)
{
perror("malloc error");
exit(-1);
}
pNode->data = rand()%1000;
printf("P[%d]:[%d]\n", n, pNode->data);
//加锁
pthread_mutex_lock(&mutex);
pNode->next = head;
head = pNode;
//解锁
pthread_mutex_unlock(&mutex);
//通知消费者线程解除阻塞
pthread_cond_signal(&cond);
sleep(rand()%3);
}
}
//消费者线程
void *consumer(void *arg)
{
NODE *pNode = NULL;
int n = *(int *)arg;
while(1)
{
//加锁
pthread_mutex_lock(&mutex);
if(head==NULL)
{
//若条件不满足,需要阻塞等待
//若条件不满足,则阻塞等待并解锁;
//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁
pthread_cond_wait(&cond, &mutex);
}
//007
printf("C[%d]:[%d]\n", n, head->data);
pNode = head;
head = head->next;
//解锁
pthread_mutex_unlock(&mutex);
free(pNode);
pNode = NULL;
sleep(rand()%3);
}
}
int main()
{
int ret;
int i = 0;
pthread_t thread1[5];
pthread_t thread2[5];
//初始化互斥锁
pthread_mutex_init(&mutex, NULL);
//条件变量初始化
pthread_cond_init(&cond, NULL);
int arr[5];
for(i=0; i<5; i++)
{
arr[i]= i;
//创建生产者线程
ret = pthread_create(&thread1[i], NULL, producer, &arr[i]);
if(ret!=0)
{
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
//创建消费者线程
ret = pthread_create(&thread2[i], NULL, consumer, &arr[i]);
if(ret!=0)
{
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
}
//等待线程结束
for(i=0; i<5; i++)
{
pthread_join(thread1[i], NULL);
pthread_join(thread2[i], NULL);
}
//释放互斥锁
pthread_mutex_destroy(&mutex);
//释放条件变量
pthread_cond_destroy(&cond);
return 0;
}
编译代码gcc -o a.out 03-pthread_cond_mul.c -lpthread
。
$ ./a.out
P[1]:[886]
P[3]:[915]
C[0]:[915]
C[1]:[886]
P[4]:[649]
P[2]:[777]
C[2]:[777]
C[1]:[649]
P[0]:[383]
C[3]:[383]
P[4]:[926]
P[4]:[426]
P[3]:[736]
C[4]:[426]
C[0]:[736]
C[0]:[926]
P[0]:[782]
C[2]:[782]
Segmentation fault (core dumped)
$
分析代码,不难发现,问题有很大概率出现在消费者代码段。
printf("C[%d]:[%d]\n", n, head->data);
pNode = head;
head = head->next;
当pthread_cond_wait
唤醒多个线程,先消费的处导致head->data
访问出错。代码修改如下
if(head==NULL)
{
//若条件不满足,需要阻塞等待
//若条件不满足,则阻塞等待并解锁;
//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁
pthread_cond_wait(&cond, &mutex);
}
if(head==NULL)
{
//解锁
pthread_mutex_unlock(&mutex);
continue;
}
总结
多个生成者和多个消费者程序在执行的时候core掉的原因分析:
假若只有一个生产者生产了一个节点, 此时会调用pthread_cond_signal通知
消费者线程, 此时若有多个消费者被唤醒了, 则最终只有消费者获得锁, 然后进行
消费, 此时会将head置为NULL, 然后其余的几个消费者线程只会有一个线程获得锁,
然后读取head的内容就会core掉.
在使用条件变量的线程中, 能够引起线程的阻塞的地方有两个:
1 在条件变量处引起阻塞---->这个阻塞会被pthread_cond_signal解除阻塞
2 互斥锁也会使线程引起阻塞----->其他线程解锁会使该线程解除阻塞.