当一个线程互斥的访问某个变量时,它可能发现在其他线程改变状态之前,它什么也做不了。例如,一个线程访问队列时,发现队列为空,它只能等待,直到其它线程将一个节点添加到队列中,这种情况就需要用到条件变量。
一、条件变量函数
(1)初始化
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condatrr_t *restrict attr);
参数:
cond:要初始化的条件变量;
attr:NULL。
(2)销毁
int pthread_cond_destroy(pthread_cond_t *cond);
(3)等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待;
mutex:互斥量,后面详细解释。
(4)唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
【例】
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *route1(void *arg)
{
while(1){
pthread_cond_wait(&cond, &mutex);
printf("action\n");
}
}
void *route2(void *arg)
{
while(1){
pthread_cond_signal(&cond);
sleep(1);
}
}
int main(void)
{
pthread_t tid1, tid2;
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_create(&tid1, NULL, route1, NULL);
pthread_create(&tid2, NULL, route2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
运行结果:
二、 为什么pthread_cond_wait需要互斥量?
1)条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
2)条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。
【例】错误的设计——先上锁,发现条件不满足,解锁,然后等待在条件变量上
pthread_mutex_lock(&mutex);
while(condition_is_false){
pthread_mutex_unlock(&mutex);
//解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
pthread_cond_wait(&cond);
pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);
1)由于解锁和等待不是原子操作。调用解锁之后,pthread_cond_wait之前,如果已经有其他线程获取到互斥量,摒弃条件满足,那么pthread_cond_wait将错过这个信号,可能会导致线程阻塞在这个pthread_cond_wait。所以解锁和等待必须是一个原子操作。
2)int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);进入该函数后,会去看条件量等于0不?等于,就把互斥量变成1,直到cond_wait返回后,把条件量改成1,把互斥量恢复成原样。
条件变量使用规范
1)等待条件代码
pthread_mutex_lock(&mutex);
while(条件为假)
pthread_cond_wait(&cond);
修改条件
pthread_mutex_unlock(&mutex);
2)给条件发送信号代码
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);
【例】生产者和消费者模型
图示如下:
代码如下
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#define CONSUMERS_COUNT 2
#define PRODUCERS_COUNT 2
struct msg{
struct msg *next;
int num;
};
struct msg *head = NULL;
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_t threads[CONSUMERS_COUNT+PRODUCERS_COUNT];
void *consumer(void *p)
{
int num = *(int *)p;
free(p);
struct msg *mp;
for( ; ; ){
pthread_mutex_lock(&mutex);
while(head == NULL){
printf("%d end wait a condition...\n", num);
pthread_cond_wait(&cond, &mutex);
}
printf("%d end wait a condition...\n", num);
printf("%d begin consumer product...\n", num);
mp = head;
head = mp -> next;
pthread_mutex_unlock(&mutex);
printf("Consume %d\n", mp->num);
free(mp);
printf("%d end consume product...\n", num);
sleep(rand()%5);
}
}
void *producer(void *p)
{
struct msg *mp;
int num = *(int *)p;
free(p);
for( ; ; ){
printf("%d begin produce product...\n", num);
mp = (struct msg*)malloc(sizeof(struct msg));
mp ->num = rand()%1000 + 1;
printf("produce %d\n", mp->num);
pthread_mutex_lock(&mutex);
mp -> next = head;
head = mp;
printf("%d end produce product...\n", num);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
sleep(rand()%5);
}
}
int main()
{
srand(time(NULL));
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
int i;
for(i=0; i<CONSUMERS_COUNT; i++)
{
int *p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&threads[i], NULL, consumer, (void*)p);
}
for(i=0; i<PRODUCERS_COUNT; i++)
{
int *p = (int*)malloc(sizeof(int));
*p = i;
pthread_create(&threads[CONSUMERS_COUNT+i], NULL, producer, (void*)p);
}
for(i = 0; i<CONSUMERS_COUNT; i++)
pthread_join(threads[i], NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
运行结果为: