条件变量不是锁,但通常结合锁使用,条件变量用于检查某个条件是否满足。
条件变量基本函数
int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);// 动态初始化条件变量,参数cond:条件变量的指针,参数attr:条件变量的初始属性。成功返回0,失败错误号。
int pthread_cond_destory(pthread_cond_t *cond);// 销毁条件变量,参数cond:条件变量的指针。成功返回0,失败错误号。
int pthread_cond_signal(pthread_cond_t *cond);// 条件满足后,可以使用该函数通知至少一个线程条件已满足,线程收到通知后会被唤醒
int pthread_cond_broadcast(pthread_cond_t *cond);// 条件满足后广播通知所有线程。pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 静态初始化
pthread_cond_wait函数
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
// 阻塞式等待条件变量cond满足,阻塞的同时释放互斥量mutex,等到满足条件后,wait返回,同时对mutex加锁。成功返回0,失败错误号。
pthread_cond_wait函数中的阻塞式检查条件满足和释放互斥锁两个操作一起被作为一个原子操作。释放互斥锁后,解除阻塞,重新加锁。
pthread_cond_timewait函数
int pthread_cond_timewait(phthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); // 成功返回0,失败错误号
作用:在某个时间内阻塞式等待条件成立,若超时则条件不满足也返回并唤醒线程。
条件变量在生产者消费者模型中的应用:
在生产者消费者问题中,若缓冲区为空,则消费者无法消费,于是消费者线程应使用pthread_cond_wait阻塞式等待条件:缓冲区不为空满足。生产者生产一个产品后,缓冲区不为空条件满足,于是使用pthread_cond_signal唤醒之前由于缓冲区为空而被阻塞的消费者线程。
生产者消费者问题中,条件变量对应的互斥锁用于使各生产者、消费者互斥地访问临界资源(本例中即缓冲区)。
流程为:
消费者:
- 创建一个条件变量:pthread_cond_t cond 并初始化条件变量:pthread_cond_init -该操作是全局的
- 创建一个互斥锁:pthread_mutex_t mutex 并初始化互斥锁:pthread_mutex_init -该操作是全局的
- 加锁:pthread_mutex_lock;
- 消费者线程使用pthread_cond_wait阻塞式等待缓冲区不为空的条件发生,在wait函数中,若消费者线程阻塞,则wait会对互斥量mutex解锁,以使生产者线程得以访问缓冲区。注意:此时pthread_cond_wait函数并未结束,而是在阻塞中,等待生产者生产一个产品。
-----pthread_cond_wait函数中阻塞与解锁作为一个原子操作是为了防止阻塞后立即被其他线程占领CPU但由于互斥锁未释放,缓冲区资源由于互斥锁无法被访问,而导致死锁的情况发生。
- 若消费者线程要访问的缓冲区为空,生产者线程生产一个产品放入缓冲区,使用pthread_cond_signal 唤醒刚刚进入阻塞的消费者线程。
- 若消费者线程要访问的缓冲区不为空,消费者线程直接被唤醒;
- 消费者线程的wait函数收到唤醒消息后,wait函数对缓冲区加锁(改mutex为加锁状态),之后,消费者线程访问共享数据(缓冲区)。
- 解锁pthread_mutex_unlock,释放条件变量pthread_cond_destory,释放锁pthread_mutex_destory。
生产者:
- 生产数据;
- 加锁pthread_mutex_lock;
- 将数据放入缓冲区;
- 解锁pthread_mutex_unlock,并通知阻塞中的消费者线程pthread_cond_signal/pthread_cond_boardcast。
- 循环生产后续数据.
程序示例1:
代码:
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<stdio.h>struct msg{struct msg *next;int num;
};struct msg *head;pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void* consumer(void* p){struct msg *mp;for(;;){pthread_mutex_lock(&lock);while(head == NULL){pthread_cond_wait(&has_product, &lock);}// 消费一个产品mp = head;head = mp->next;pthread_mutex_unlock(&lock);printf("-Consume %lu ---%d\n", pthread_self(), mp->num);free(mp);sleep(rand() % 5);}}void* producer(void* p){struct msg *mp;for(;;){mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1;printf("-Produce ----------------%d\n", mp->num);pthread_mutex_lock(&lock);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));for(int i = 0; i < 3; i++){pthread_create(&pid, NULL, producer, NULL);}for(int i = 0; i < 3; i++){pthread_create(&cid, NULL, consumer, NULL);}pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}
执行结果:
程序示例2:
代码:
#include<pthread.h>const int BUFFER_MAXNUM = 3;pthread_cond_t empty_cond = PTHREAD_COND_INITIALIZER, full_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;int empty = BUFFER_MAXNUM, full = 0;int cnt = 0;void* productor(void* arg){while(1){pthread_mutex_lock(&mutex);--empty;if(empty < 0){printf("生产者%d 等待缓冲区不为满\n", (int)arg);pthread_cond_wait(&full_cond, &mutex);printf("生产者%d 已等到缓冲区不为满\n", (int)arg);}// 生产printf("生产者%d, product %dth\n", (int)arg, ++cnt);// 将数据放入缓冲区if(++full <= 0){pthread_cond_signal(&empty_cond);}pthread_mutex_unlock(&mutex);sleep(1);}
}void* consumer(void* arg){while(1){pthread_mutex_lock(&mutex);if(--full < 0){printf("消费者%d 等待缓冲区不为空\n", (int)arg);pthread_cond_wait(&empty_cond, &mutex);printf("消费者%d 已等到缓冲区不为空\n", (int)arg);}// 消费一个产品printf("消费者%d, cast %dth\n", (int)arg, cnt--);if(++empty <= 0){pthread_cond_signal(&full_cond);}pthread_mutex_unlock(&mutex);sleep(1);}
}int main(){pthread_t pdr, csr;for(int i = 0; i < 3; i++){pthread_create(&pdr, NULL, productor, (void*)i);}for(int i = 0; i < 5; i++){pthread_create(&csr, NULL, consumer, (void*)i);}pthread_exit(NULL);
}