文章目录
概述
条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用
当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待
函数使用
头文件
#include <pthread.h>
函数模型
pthread_cond_init()
pthread_cond_wait()
pthread_cond_timedwait()
pthread_cond_signal()
pthread_cond_broadcast()
pthread_cond_destroy()
说明:
- 以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。
pthread_cond_init
- 初始化一个条件变量
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参数说明:
- cond:传出参数,调用前在外面先创建条件变量,pthread_cond_t cond;
- attr:attr表条件变量属性,通常为默认值,传NULL即可
说明:
- 可以使用静态初始化的方法,初始化条件变量:
- 静态初始化条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_destroy
- 销毁一个条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_wait
- 阻塞等待一个条件变量
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
pthread_cond_timedwait
- 阻塞等待一个条件变量
int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
pthread_mutex_t *__restrict __mutex, const struct timespec *__restrict __abstime)
参数:
说明:
阻塞等待条件变量cond(参1)满足;
释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex)(1.2.两步为一个原子操作)
当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);(如果锁被其它线程抢走了,那么当前线程会继续阻塞,如果抢到锁了,那么就可以继续往下执行)
pthread_cond_signal
- 唤醒至少一个阻塞在条件变量上的线程
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast
- 唤醒全部阻塞在条件变量上的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
条件变量的优点:
- 相较于mutex而言,条件变量可以减少竞争。
- 如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果链表中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。
代码示例
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//节点结构体
struct msg {
int num; //数据区
struct msg *next; //链表区
};
struct msg *head = NULL; //头指针
struct msg *mp = NULL; //节点指针
//利用宏定义的方式初始化全局的互斥锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
void *producter(void *arg) {
while (1) {
mp = malloc(sizeof(struct msg));
mp->num = rand() % 400 + 1;
printf("---producted---%d\n", mp->num);
pthread_mutex_lock(&mutex); //访问共享区域必须加锁
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&has_product); //通知消费者来消费
sleep(rand() % 3);
}
return NULL;
}
void *consumer(void *arg) {
while (1) {
pthread_mutex_lock(&mutex); //访问共享区域必须加锁
while (head == NULL) //如果共享区域没有数据,则解锁并等待条件变量
{
pthread_cond_wait(&has_product, &mutex);
}
mp = head;
head = mp->next;
pthread_mutex_unlock(&mutex);
printf("------------------consumer--%d\n", mp->num);
free(mp); //释放被删除的节点内存
mp = NULL; //并将删除的节点指针指向NULL,防止野指针
sleep(rand() % 3);
}
return NULL;
}
int main(void) {
pthread_t ptid, ctid;
//创建生产者和消费者线程
pthread_create(&ptid, NULL, producter, NULL);
pthread_create(&ctid, NULL, consumer, NULL);
//主线程回收两个子线程
pthread_join(ptid, NULL);
pthread_join(ctid, NULL);
return 0;
}