Linux多线程C++版(八) 线程同步方式-----条件变量
创始人
2024-03-13 18:39:26
0

目录

        • 1.条件变量基本概念
        • 2.条件变量创建和销毁
        • 3.条件变量等待操作
        • 4.条件变量通知(唤醒)操作
        • 5.代码了解线程同步
        • 6.线程的状态转换
        • 7.代码改进--从一对一到一对多

1.条件变量基本概念

  • 互斥锁的缺点是它只有两种状态:锁定和非锁定
  • 条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足
  • 条件变量内部是一个等待队列,放置等待线程,线程在条件变量上等待和通知,互斥锁用来保护等待队列(对等待队列上锁),条件变量通常和互斥锁一起使用。
  • 条件变量允许线程等待特定条件发生,当条件不满足时,线程通常先进入阻塞状态,等待条件发生变化。一旦其他的某个线程改变了条件,可唤醒一个或多个阻塞的线程
  • 具体的判断条件还需要用户给出
  • 条件变量数据类型 pthread_cond_t

2.条件变量创建和销毁

//条件变量的定义
pthread_cond_t cond;
int pthread_cond_init(pthread_cond_t *restrict cond,pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *restrict cond);
返回:成功返回0 出错返回错误编号
  • 参数
    • cond:条件变量
    • attr:条件变量属性

3.条件变量等待操作

//线程等待
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
//线程等待一段时间,如果到时间就返回
int pthread_cond_timewait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict timeout);
返回:成功返回0 出错返回错误编号
struct timespec{time_t tv_sec;// secondslong tv_nsec //nanoseconds
}
  • 参数
    • cond:条件变量
    • mutex:互斥锁
  • 互斥锁mutex是对条件变量cond的保护
  • 线程由于调用wait函数阻塞,否则释放互斥锁

4.条件变量通知(唤醒)操作

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
  • 参数
    • cond:条件变量
  • 当条件满足,线程需要通知(唤醒)等待的线程
  • pthread_cond_signal函数通知(唤醒)单个线程
  • pthread_cond_broadcast函数通知(唤醒)所有线程

5.代码了解线程同步

#include
#include
#include
/*一个线程赋值计算结果,一个线程负责获取结果当计算结果的线程没有执行完毕,获取结果的线程要等待(阻塞)
*/
typedef struct{//共享资源int res;//存放运算结果int is_wait;//用户给出用于判断的条件pthread_cond_t cond;//定义条件变量pthread_mutex_t mutex;//定义互斥锁
}Result;//计算并将结果放置Result中的线程运行函数
void* set_fn(void *arg){int sumfor(int i =1;i<=100;i++){sum +=i;}//将结果存放到ResultResult *r = (Result*)arg;r->res = sum;//对两个线程共享的判断条件进行保护pthread_mutex_lock(&r-mutex);//判断获取结果的线程是否准备好,只有准备好了才可结束。is_wait为0代表没有准备好while(!r->is_wait){//等待获取结果的线程pthread_mutex_unlock(&r-mutex);Sleep(10000);pthread_mutex_lock(&r-mutex);}pthread_mutex_unlock(&r-mutex);//唤醒(通知)等待的那个获取结果的线程pthread_cond_broadcast(&r->cond)return(void*)0;
}//获得结果的线程运行函数
void* get_fn(void *arg){Result *r = (Result*)arg;//对两个线程共享的判断条件进行保护(加锁)//两个线程对判断条件操作是互斥的pthread_mutex_lock(&r-mutex);//代表获取结果的线程已经准备好了r->is_wait = 1;//获取结果的线程等待,就是自身线程阻塞//&r->mutex这个锁是用于保护队列pthread_cond_wait(&r->cond,&r->mutex);//线程唤醒后,释放锁pthread_mutex_unlock(&r->mutex);int res = r->res;printf("ox%lx get sum is %d\n",pthread_self(),res);return(void*)0;
}int main(void){int err;//定义线程标识符cal getpthread_t cal, get;//结构体赋值Result r;r.is_wait = 0;//条件变量,互斥锁初始化pthread_cond_init(&r.cond,null);pthread_mutex_init(&r.mutex,null);//创建两个线程,并给与赋值//启动获取结果的线程if ((err = pthread_create(&get, NULL, get_fn, (void*)&r)) != 0) {perror("pthread_create error");}//启动计算结果的线程if ((err = pthread_create(&cal, NULL, set_fn, (void*)&r)) != 0) {perror("pthread_create error");}//主线程要等两个子线程执行完毕pthread_join(get,null);pthread_join(cal,null);//条件变量和互斥锁的销毁pthread_cond_destroy(&r.cond);pthread_mutex_destroy(&r.mutex);return 0;
}

程序结果输出:

在这里插入图片描述

对pthread_cond_wait函数为什么在后面要加上 释放锁

//对两个线程共享的判断条件进行保护(加锁)
//两个线程对判断条件操作是互斥的pthread_mutex_lock(&r-mutex);
//代表获取结果的线程已经准备好了r->is_wait = 1;//获取结果的线程等待,就是自身线程阻塞
//&r->mutex这个锁是用于保护队列pthread_cond_wait(&r-cond,&r->mutex);
//线程唤醒后,释放锁pthread_mutex_unlock(&r-mutex);

在这里插入图片描述

为什么pthread_cond_wait(&r-cond,&r->mutex)线程等待函数要在,释放锁函数的前面?

​ 正如图片所展示的,在执行pthread_cond_wait时第一步先锁释放(这个释放锁和上面那个加锁是一对的),同时又上锁(此时上锁是用于保护线程存放到等待队列),线程自己插入条件变量的等待队列中,此时再解锁等待唤醒,唤醒后在上锁和最后那个释放锁是一对的。

6.线程的状态转换

在这里插入图片描述

7.代码改进–从一对一到一对多

#include
#include
#include
/*一个线程赋值计算结果,多个线程负责获取结果当计算结果的线程没有执行完毕,获取结果的线程要等待(阻塞)
*/
typedef struct{//共享资源int res;//存放运算结果int couter;//用于统计获取结果线程的数量pthread_cond_t cond;//定义条件变量pthread_mutex_t mutex;//定义互斥锁
}Result;//计算并将结果放置Result中的线程运行函数
void* set_fn(void *arg){int sumfor(int i =1;i<=100;i++){sum +=i;}//将结果存放到ResultResult *r = (Result*)arg;r->res = sum;//对两个线程共享的判断条件进行保护pthread_mutex_lock(&r-mutex);//判断获取结果的线程是否多余2个,只有2个以上的线程了才可继续运行。否则就等待while(r->couter< 2){//等待获取结果的线程pthread_mutex_unlock(&r-mutex);Sleep(10000);pthread_mutex_lock(&r-mutex);}pthread_mutex_unlock(&r-mutex);//唤醒(通知)等待的那个获取结果的线程pthread_cond_broadcast(&r->cond)return(void*)0;
}//获得结果的线程运行函数
void* get_fn(void *arg){Result *r = (Result*)arg;//对两个线程共享的判断条件进行保护(加锁)//两个线程对判断条件操作是互斥的pthread_mutex_lock(&r-mutex);//代表获取结果的线程已经准备好了r->couter ++;//获取结果的线程等待,就是自身线程阻塞//&r->mutex这个锁是用于保护队列pthread_cond_wait(&r-cond,&r->mutex);//线程唤醒后,释放锁pthread_mutex_unlock(&r-mutex);int res = r->res;printf("ox%lx get sum is %d\n",pthread_self(),res);return(void*)0;
}int main(void){int err;//定义线程标识符cal getpthread_t cal,get1,get1;//结构体赋值Result r;r.couter = 0;//条件变量,互斥锁初始化pthread_cond_init(&r.cond,null);pthread_mutex_init(&r.mutex,null);//启动获取结果的线程两个if ((err = pthread_create(&get1, NULL, get_fn, (void*)&r)) != 0) {perror("pthread_create error");}if ((err = pthread_create(&get2, NULL, get_fn, (void*)&r)) != 0) {perror("pthread_create error");}//启动计算结果的线程if ((err = pthread_create(&cal, NULL, set_fn, (void*)&r)) != 0) {perror("pthread_create error");}//主线程要等两个子线程执行完毕pthread_join(get1,null);pthread_join(get2,null);pthread_join(cal,null);//条件变量和互斥锁的销毁pthread_cond_destroy(&r.cond);pthread_mutex_destroy(&r.mutex);return 0;
}

代码运行结果:
在这里插入图片描述

相关内容

热门资讯

VITURE与XREAL专利纠... 文/VR陀螺 12月圣诞假期前夕,一则有关VITURE和XREAL的专利纠纷,将两家AR头部厂商卷入...
*ST奥维及控股子公司新增诉讼... 12月25日,*ST奥维(002231)发布公告,截至本公告披露日,公司及控股子公司在连续12个月内...
“十四五”时期临沂市深化医保制... 大众网记者王萍 临沂报道 12月25日,临沂市人民政府新闻办公室召开临沂市“高质量完成‘十四五’规...
平顶山学院:一堂聚焦矛盾调解的... 为深化法治校园建设,提升师生法治意识与基层治理认知,12月23日下午,平顶山市公安局新华分局“老贺调...
原创 6... 在充满变数的时代浪潮中,历史的阴影始终笼罩着东亚大地。日前,日本解密的6800多页外交档案犹如一把尖...
浙江出台首部海洋经济省级综合性... 记者12月25日从浙江省海洋经济发展厅获悉,近日浙江省十四届人大常委会第二十一次会议审议并通过《浙江...
比亚迪起诉自媒体“龙哥讲电车”... IT之家 12 月 25 日消息,今日比亚迪法务部官微发文称,近期,就比亚迪起诉“龙哥讲电车”、“满...
为解燃眉之急,珠海法官将三地6... “我们的账户解冻了,公司有救了!” 拿到账户解封通知的那一刻,某建筑公司负责人李某悬了几个月的心终于...
泽大律师事务所律师马恺浓:快手... 12月22日晚间,快手平台突发大规模内容安全事件,多个直播间短时间内涌入大量露骨色情内容,违规内容传...
江苏首部民营经济专项法规,明年... 新悉,《南通市人民代表大会常务委员会关于促进民营经济高质量发展的决定》将于2026年1月1日起正式施...