合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
[TOC] # 简介 与互斥锁不同,条件变量是用来等待而不是用来上锁的,**条件变量本身不是锁**! 条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。 条件变量的两个动作: * 条件不满, 阻塞线程 * 当条件满足, 通知阻塞的线程开始工作 条件变量的类型: pthread\_cond\_t。 # pthread_cond_init函数 ~~~ #include <pthread.h> ​ int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 功能: 初始化一个条件变量 参数: cond:指向要初始化的条件变量指针。 attr:条件变量属性,通常为默认值,传NULL即可 也可以使用静态初始化的方法,初始化条件变量: pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 返回值: 成功:0 失败:非0错误号 ~~~ # pthread_cond_destroy函数 ~~~ #include <pthread.h> ​ int pthread_cond_destroy(pthread_cond_t *cond); 功能: 销毁一个条件变量 参数: cond:指向要初始化的条件变量指针 返回值: 成功:0 失败:非0错误号 ~~~ # pthread_cond_wait函数 ~~~ #include <pthread.h> ​ int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 功能: 阻塞等待一个条件变量 a) 阻塞等待条件变量cond(参1)满足 b) 释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex); a) b) 两步为一个原子操作。 c) 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex); ​ 参数: cond:指向要初始化的条件变量指针 mutex:互斥锁 ​ 返回值: 成功:0 失败:非0错误号 ~~~ ~~~ int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct .*restrict abstime); 功能: 限时等待一个条件变量 ​ 参数: cond:指向要初始化的条件变量指针 mutex:互斥锁 abstime:绝对时间 ​ 返回值: 成功:0 失败:非0错误号 ~~~ abstime补充说明: ~~~ struct timespec { time_t tv_sec; /* seconds */ // 秒 long tv_nsec; /* nanosecondes*/ // 纳秒 } ​ time_t cur = time(NULL); //获取当前时间。 struct timespec t; //定义timespec 结构体变量t t.tv_sec = cur + 1; // 定时1秒 pthread_cond_timedwait(&cond, &t); ~~~ # pthread_cond_signal函数 唤醒至阻塞在条件变量上的线程 ~~~ #include <pthread.h> ​ int pthread_cond_signal(pthread_cond_t *cond); 功能: 唤醒至少一个阻塞在条件变量上的线程 参数: cond:指向要初始化的条件变量指针 返回值: 成功:0 失败:非0错误号 ​ int pthread_cond_broadcast(pthread_cond_t *cond); 功能: 唤醒全部阻塞在条件变量上的线程 参数: cond:指向要初始化的条件变量指针 返回值: 成功:0 失败:非0错误号 ~~~ # 生产者消费者条件变量模型 线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。 假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。 ~~~ // 节点结构 typedef struct node { int data; struct node* next; }Node; ​ // 永远指向链表头部的指针 Node* head = NULL; ​ // 线程同步 - 互斥锁 pthread_mutex_t mutex; // 阻塞线程 - 条件变量类型的变量 pthread_cond_t cond; ​ // 生产者 void* producer(void* arg) { while (1) { // 创建一个链表的节点 Node* pnew = (Node*)malloc(sizeof(Node)); // 节点的初始化 pnew->data = rand() % 1000; // 0-999 ​ // 使用互斥锁保护共享数据 pthread_mutex_lock(&mutex); // 指针域 pnew->next = head; head = pnew; printf("====== produce: %lu, %d\n", pthread_self(), pnew->data); pthread_mutex_unlock(&mutex); ​ // 通知阻塞的消费者线程,解除阻塞 pthread_cond_signal(&cond); ​ sleep(rand() % 3); } return NULL; } ​ void* customer(void* arg) { while (1) { pthread_mutex_lock(&mutex); // 判断链表是否为空 if (head == NULL) { // 线程阻塞 // 该函数会对互斥锁解锁 pthread_cond_wait(&cond, &mutex); // 解除阻塞之后,对互斥锁做加锁操作 } // 链表不为空 - 删掉一个节点 - 删除头结点 Node* pdel = head; head = head->next; printf("------ customer: %lu, %d\n", pthread_self(), pdel->data); free(pdel); pthread_mutex_unlock(&mutex); } return NULL; } ​ int main(int argc, const char* argv[]) { pthread_t p1, p2; // init pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); ​ // 创建生产者线程 pthread_create(&p1, NULL, producer, NULL); // 创建消费者线程 pthread_create(&p2, NULL, customer, NULL); ​ // 阻塞回收子线程 pthread_join(p1, NULL); pthread_join(p2, NULL); ​ pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); ​ return 0; } ~~~ # 条件变量的优缺点 相较于mutex而言,条件变量可以减少竞争。 如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。 有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。