Linux网络编程【条件变量】
【摘要】 05. 条件变量 5.1 条件变量概述与互斥锁不同,条件变量是用来等待而不是用来上锁的,条件变量本身不是锁!条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量的两个动作:条件不满, 阻塞线程当条件满足, 通知阻塞的线程开始工作条件变量的类型: pthread_cond_t。 5.2 pthread_cond_init函数#include <pthr...
05. 条件变量
5.1 条件变量概述
与互斥锁不同,条件变量是用来等待而不是用来上锁的,条件变量本身不是锁!
条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
条件变量的两个动作:
- 条件不满, 阻塞线程
- 当条件满足, 通知阻塞的线程开始工作
条件变量的类型: pthread_cond_t。
5.2 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错误号
5.3 pthread_cond_destroy函数
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
功能:
销毁一个条件变量
参数:
cond:指向要初始化的条件变量指针
返回值:
成功:0
失败:非0错误号
5.4 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
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);
5.5 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错误号
5.6条件变量应用示例
//每隔两秒钟修改条件
int flag = 0;//判断条件标志
pthread_cond_t cond;//互斥量
pthread_mutex_t mutex;//条件变量
//改变条件的线程
void* fun1(void *arg)
{
while(1)
{
//加锁
pthread_mutex_lock(&mutex);
flag = 1;
//解锁
pthread_mutex_unlock(&mutex);
//唤醒因为条件而阻塞线程
pthread_cond_signal(&cond);
sleep(2);
}
return NULL;
}
//等待条件的线程
void* fun2(void* arg)
{
while(1)
{
//加锁
pthread_mutex_lock(&mutex);
//表示条件不满足
if(0 == flag)
{
//等待条件满足 会阻塞
printf("条件暂时不满足,需等待一会...\n");
pthread_cond_wait(&cond,&mutex);
}
printf("线程2因为满足条件 开始运行...\n");
flag = 0;
//解锁
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_t tid1,tid2;;
int ret = -1;
//初始化互斥锁
ret = pthread_mutex_init(&mutex,NULL);
if(0 != ret)
{
printf("pthread_mutex_init failed...\n");
return 1;
}
//初始化条件变量
ret = pthread_cond_init(&cond,NULL);
if(0 != ret)
{
printf("pthread_cond_init failed...\n");
return 1;
}
//创建两个线程
ret = pthread_create(&tid1,NULL,fun1,NULL);
if(0 != ret)
{
printf("pthread_create failed...\n");
return 1;
}
ret = pthread_create(&tid2,NULL,fun2,NULL);
if(0 != ret)
{
printf("pthread_create failed...\n");
return 1;
}
//回收两个线程资源
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
//销毁
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
5.6 生产者消费者条件变量模型
线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。
假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
//生产者与消费者模型
//连表节点类型
typedef struct _node_t
{
int date; //数据类
struct _node_t* next; //指针域
}node_t;
node_t* head = NULL; //头节点
pthread_mutex_t mutex; //互斥量
pthread_cond_t cond; //条件变量
//生产者
void* procuder(void *arg)
{
node_t* new = NULL; //新节点
//加锁
pthread_mutex_lock(&mutex);
//循环生产产品
while(1)
{
node_t* new = malloc(sizeof(node_t)); //分配节点空间(使用堆会出现不可重入问题,需要使用锁)
if(new == NULL)
{
printf("malloc failed.../n");
break;
}
memset(new,0,sizeof(node_t));
new->date = random()%100+1;//赋值:1-100
new->next = NULL;
printf("生产者生产产品:%d\n",new->date);
//头插法
new->next = head;
head = new;
pthread_mutex_unlock(&mutex); //解锁
//唤醒因为i条件变量而阻塞的线程
pthread_cond_signal(&cond);
sleep(random()%3+1);//随机睡眠1-3秒
}
pthread_exit(NULL);
}
//消费者线程
void *customer(void* arg)
{
node_t* tmp = NULL; //临时节点
pthread_mutex_lock(&mutex); //加锁
//循环消费
while(1)
{
//链表为空的情形
if(head == NULL)
{
//如果链表为空 就阻塞
pthread_cond_wait(&cond,&mutex);
}
else
{
//第一个节点地址赋值给临时变量tmp
tmp = head;
//head指向链表的第二个节点
head = head->next;
printf("消费者消耗产品:%d\n",tmp->date);
free(tmp); //释放空间
pthread_mutex_unlock(&mutex); //解锁
sleep(random()%3+1);//随机睡眠1-3秒
}
}
return NULL;
}
//生产者和消费者模型 条件变量的模型
int main(void)
{
int ret = -1;
srandom(getpid()); //设置随机种子
pthread_t tid1 = -1, tid2 = -1;
//初始化互斥锁
ret = pthread_mutex_init(&mutex,NULL);
if(0 != ret)
{
printf("pthread_mutex_init falied...\n");
}
//初始化条件变量
ret = pthread_cond_init(&cond,NULL);
if(0 != ret)
{
printf("pthread_cond_init falied...\n");
}
//创建两个线程
ret = pthread_create(&tid1,NULL,procuder,NULL);
if(0 != ret)
{
printf("pthread_create failed...\n");
return 1;
}
ret = pthread_create(&tid2,NULL,customer,NULL);
if(0 != ret)
{
printf("pthread_create failed...\n");
return 1;
}
//等待两个线程结束,回收资源
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
//销毁互斥锁
pthread_mutex_destroy(&mutex);
//销毁条件变量
pthread_cond_destroy(&cond);
return 0;
}
运行结果:
xcc@ubuntu:~/9th$ ./a.out
生产者生产产品:43
消费者消耗产品:43
生产者生产产品:51
消费者消耗产品:51
生产者生产产品:97
消费者消耗产品:97
生产者生产产品:94
消费者消耗产品:94
生产者生产产品:89
生产者生产产品:14
消费者消耗产品:14
消费者消耗产品:89
生产者生产产品:35
消费者消耗产品:35
生产者生产产品:76
消费者消耗产品:76
生产者生产产品:84
消费者消耗产品:84
生产者生产产品:87
消费者消耗产品:87
生产者生产产品:36
消费者消耗产品:36
生产者生产产品:89
消费者消耗产品:89
^C
5.7 条件变量的优缺点
相较于mutex而言,条件变量可以减少竞争。
如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。
有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)