【Linux】线程同步条件变量

目录

1 线程同步的引入

2 条件变量&线程同步&竞争条件的概念

3 条件变量相关函数

初始化

销毁

等待条件满足

唤醒等待

4 demo代码——理解条件变量&线程同步

5 为什么 pthread_cond_wait 需要互斥量?

6 条件变量使用规范


1 线程同步的引入

例子生活化:

学校里有一间VIP自习室,只能让一个人在里面自习,为了保护使用者自习不被他人所打扰,配备以一把锁。张三是一个勤奋的学生,为了抢到VIP自习室的使用权,凌晨六点就已经到达了自习室开始了自习,并将自习室给锁上了。后面陆续过来的人就只能在自习室外面等待。某个时刻张三想要上厕所,为了不让别人在此期间进入自习室,出门的时候反手就把门给锁上了,所以在张三上厕所的期间,张三自习室里面自己的学习资料就可得到很好的保护起来。第一个阶段:多线程访问临界资源的加锁保护,做到了互斥张三上完厕所回来了,继续之前的自习活动。过了一会,张三由于没有吃早饭,肚子开始饿了起来,打算结束自习状态去吃东西。所以张三离开自习室,刚刚放下钥匙的时候,心里又想着吃完饭回来就可能要等待自习室的了,觉得不好,所以想要回去自习。由于钥匙和张三此时离的很近,之前等待钥匙的人离钥匙较远,所以此时竞争钥匙的时候,张三的竞争力大,又重新拿到了钥匙。所以其他人只能继续等待。接着,张三用钥匙开启了VIP自习室的门,刚把门锁上,张三的肚子又饿了起来,又重复了之前的动作。由于食物的资源在未来的一段时间里面,都无法到达,所以张三就一直在重复着锁与解锁的动作,没有进入学习的状态,而别人也没有得到锁的机会,从而也无法学习。第二阶段:多线程按照规则锁与解锁,没错,但是不合理)后来,校方发现了VIP自习室的这个不合理的地方,就规定了:自习完毕的人,归还完钥匙之后,不能立即申请钥匙锁,而必须重新排队,在外面人也要进行等待排队。——在安全的规则下,多线程访问资源具有一定的顺序性,为了合理解决饥饿问题;提出了线程同步,让多线程协同工作!

2 条件变量&线程同步&竞争条件的概念

  • 条件变量
    • 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
    • 例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。
  • 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步
  • 竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也不难理解

3 条件变量相关函数

初始化

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);
        参数:
                 cond:要初始化的条件变量
                 attr:NULL(条件变脸的属性)

销毁

int pthread_cond_destroy(pthread_cond_t *cond)

等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
        参数:
                cond:要在这个条件变量上等待
                mutex:互斥量

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒条件队列中所有的线程
int pthread_cond_signal(pthread_cond_t *cond);//唤醒条件队列中的一个线程

4 demo代码——理解条件变量&线程同步

【解锁操作可以在任意一个线程中进行】

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void* PthreadRoutine(void* args)
{const char* name=static_cast<const char*>(args);while(true){pthread_mutex_lock(&mutex);cout<<name<<" run"<<endl;}
}
int main()
{pthread_t t[4];for(int i=0;i<4;++i){char* name=new char[64];snprintf(name,64,"thread-%d",i+1);pthread_create(t+i,nullptr,PthreadRoutine,name);}while(true){pthread_mutex_unlock(&mutex);//解锁操作可以在任意一个线程中进行sleep(1);}for(int i=0;i<4;++i){pthread_join(t[i],nullptr);}return 0;
}

【条件变量的使用】

#include <iostream>using namespace std;#include <unistd.h>
#include <pthread.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void *PthreadRoutine(void *args)
{const char *name = static_cast<const char *>(args);while (true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex); // 挂起等待,等待某个条件满足,等待其它线程唤醒该线程cout << name << " run" << endl;pthread_mutex_unlock(&mutex);}
}
int main()
{pthread_t t[4];for (int i = 0; i < 4; ++i){char *name = new char[64];snprintf(name, 64, "thread-%d", i + 1);pthread_create(t + i, nullptr, PthreadRoutine, name);}sleep(3);while (true){pthread_cond_signal(&cond); // 唤醒一个线程sleep(1);}for (int i = 0; i < 4; ++i){pthread_join(t[i], nullptr);}return 0;
}

5 为什么 pthread_cond_wait 需要互斥量?

  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
  • 条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。 

  • 按照上面的说法,我们设计出如下的代码:先上锁,发现条件不满足,解锁,然后等待在条件变量上不就行了,如下代码: 

// 错误的设计
pthread_mutex_lock(&mutex);
while (condition_is_false)

{
        pthread_mutex_unlock(&mutex);
        //解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
        pthread_cond_wait(&cond);
        pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);

  • 由于解锁和等待不是原子操作。调用解锁之后, pthread_cond_wait 之前,如果已经有其他线程获取到互斥量,摒弃条件满足,发送了信号,那么 pthread_cond_wait 将错过这个信号,可能会导致线程永远阻塞在这个 pthread_cond_wait 。所以解锁和等待必须是一个原子操作。
  • int pthread_cond_wait(pthread_cond_ t *cond,pthread_mutex_ t * mutex); 进入该函数后,会去看条件量等于0不?等于,就把互斥量变成1,直到cond_ wait返回,把条件量改成1,把互斥量恢复成原样。

6 条件变量使用规范

等待条件代码

pthread_mutex_lock(&mutex);
while (条件为假)//细节:解除等待的时候,判断条件是否满足,确保解除等待后任何时候条件都是满足的
        pthread_cond_wait(cond, mutex);
修改条件
pthread_mutex_unlock(&mutex);

给条件发送信号代码

pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/11555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【多任务编程-线程通信】

进程/线程通信的方式 某些应用程序中&#xff0c;进程/进程和线程/线程之间不可避免的进行通信&#xff0c;进行消息传递&#xff0c;数据共享等 同一进程的线程之间通信方式包括Windows中常用Event, Message等。 不同进程之间的通信可以利用Event, FileMapping(内存共享), W…

24考研数据结构-栈和队列的应用

目录 3.3.1栈在括号匹配中的应用流程图算法代码 3.3.2栈在表达式求值中的应用1. 中缀表达式 (需要界限符)2. 后缀表达式 (逆波兰表达式)中缀表达式转后缀表达式-手算重点&#xff1a;中缀表达式转后缀表达式-机算重点&#xff1a;后缀表达式的计算—机算 3.前缀表达式 (波兰表达…

杭电oj Simple Set Problem 双指针 尺取法 满注释版

&#x1f468;‍&#x1f3eb; 题目地址 输入 3 2 1 6 3 -7 7 10 4 9 -5 -9 2 8 5 4 3 3 8 2 10 8 1 -7 3 1 6 10 1 1 9输出 1 15 0使用快读&#xff0c;避免使用 Arrays.fill() 按需初始化 避免卡常 &#x1f351; 思路 &#x1f37a; AC code import java.io.*; import ja…

机器学习李宏毅学习笔记39

文章目录 前言一、大模型的发展趋势二、KNN LM总结 前言 大模型大资料 大模型的顿悟时刻 一、大模型的发展趋势 随数据量增加&#xff0c;模型可以从量变达到质变&#xff0c;从某一刻开始突然学会东西。 当成为大模型时&#xff0c;分数会从0,0突然变成100&#xff0c;完成“…

【Matplotlib 绘制折线图】

使用 Matplotlib 绘制折线图 在数据可视化中&#xff0c;折线图是一种常见的图表类型&#xff0c;用于展示随着变量的变化&#xff0c;某个指标的趋势或关系。Python 的 Matplotlib 库为我们提供了方便易用的功能来绘制折线图。 绘制折线图 下面的代码展示了如何使用 Matplo…

基于Centos 7虚拟机的磁盘操作(添加磁盘、分区、格式分区、挂载)

目录 一、添加硬盘 二、查看新磁盘 三、磁盘分区 3.1新建分区 3.2 格式分区 3.3 挂载分区 3.4 永久挂载新分区 3.5 取消挂载分区 一、添加硬盘 1.在虚拟机处选择编辑虚拟机设置&#xff0c;然后选择添加 2.选择硬盘&#xff0c;然后选择下一步 3.默认即可&#xff0c;下一步…

【6】toLocaleString、toLocaleDateString、toLocaleTimeString等toLocale系列方法的使用及案例

一、介绍 在当今前端开发的领域里&#xff0c;快速、高效的项目构建工具以及使用最新技术栈是非常关键的。ViteVue3 组合为一体的项目实战示例专栏将带领你深入了解和掌握这一最新的前端开发工具和框架。 作为下一代前端构建工具&#xff0c;Vite 在开发中的启动速度和热重载…

C语言:动态内存管理

文章目录 一、动态内存函数1. malloc2. calloc3. realloc4. free 二、常见的错误1.malloc或calloc开辟的空间未检查2.越界访问3.对非malloc和calloc开辟的空间&#xff0c;用free释放4.对同一块动态内存多次释放5.用free释放动态内存的一部分 三、通讯录(动态版本改写)总结 一、…

uni-app:模态框的实现(弹窗实现)

效果图 代码 标签 <template><view><!-- 按钮用于触发模态框的显示 --><button click"showModal true">显示模态框</button><!-- 模态框组件 --><view class"modal" v-if"showModal"><view cla…

探索APP开发的新趋势:人工智能和大数据的力量

随着5G技术的不断发展&#xff0c;人工智能和大数据将会更加广泛的应用于我们生活和工作中&#xff0c;作为 APP开发公司&#xff0c;应该及时的对新技术进行研发&#xff0c;进而更好的为用户服务。目前 APP开发已经不是传统的软件开发了&#xff0c;而是向移动互联网转型&…

iPhone 开机停留在苹果logo画面(已解决)

一、问题 如下图&#xff0c;开不了机&#xff1a; 标题 二、根因 存储空间满了。 三、解决方法 方法一 用苹果数据线&#xff08;最好是原装&#xff09;连接Mac电脑&#xff0c;在装有 macOS Catalina 10.15 或更高版本的 Mac 上&#xff0c;打开“访达”。在装有 macOS…

六、代理模式

文章目录 一、代理模式1、代理模式的好处和缺点1.1 代理模式理解加深 一、代理模式 为什么要学习代理模式&#xff1f; 代理模式是Spring AOP 以及 Spring MVC 的底层&#xff01;&#xff01;并且还是 JAVA 的23种设计模式之一&#xff01;&#xff01; 代理模式的分类&#…

opencv-27 阈值处理 cv2.threshold()

怎么理解阈值处理? 阈值处理&#xff08;Thresholding&#xff09;是一种常用的图像处理技术&#xff0c;在机器学习和计算机视觉中经常被用于二值化图像或二分类任务。它基于设定一个阈值来将像素值进行分类&#xff0c;将像素值大于或小于阈值的部分分为两个不同的类别&…

从Web2到Web3:区块链技术的未来前景

随着互联网的发展&#xff0c;Web1.0、Web2.0 和 Web3.0 成为了人们口中津津乐道的话题。那么&#xff0c;这三种网络时代究竟有什么区别呢&#xff1f; Web1.0 是一个只读的时代&#xff0c;那个时候&#xff0c;用户只能浏览网页&#xff0c;无法进行互动和创作。Web2.0 则是…

php-golang-jsonrpc2.0 rpc-codec/jsonrpc2和tivoka/tivoka实践

golang代码&#xff1a; package main import ( "context" "net" "net/rpc" "github.com/powerman/rpc-codec/jsonrpc2" ) type App struct{} type Res struct { Code int json:"code" Msg string json:"msg&quo…

系列二、RocketMQ简介

一、概述 RocketMQ是一款阿里巴巴开源的消息中间件。2016年11月28日&#xff0c;阿里巴巴向Apache软件基金会捐赠RabbitMQ&#xff0c;成为Apache孵化项目。2017年9月25日&#xff0c;Apache宣布RocketMQ孵化成为Apache顶级项目&#xff08;TLP&#xff09;&#xff0c;成为国内…

树莓派微型web服务器——阶段设计报告

文章目录 1. 需求分析1.1 功能需求1.1.1 访问需求1.1.2 自定义域名需求1.1.3 下载公共文件需求1.1.4 用户体验需求 1.2 技术需求1.2.1 操作系统指令1.2.2 技术栈1.2.3 内网穿透 1.3 性能需求1.3.1 处理能力1.3.2 内存1.3.3 存储空间 2. 可行性分析2.1 硬件方面2.2 软件方面 3. …

[Tools: Camera Conventions] NeRF中的相机矩阵估计

参考&#xff1a;NeRF代码解读-相机参数与坐标系变换 - 知乎 在NeRF中&#xff0c;一个重要的步骤是确定射线&#xff08;rays&#xff09;的初始点和方向。根据射线的初始点和方向&#xff0c;和设定射线深度和采样点数量&#xff0c;可以估计该射线成像的像素值。估计得到的…

flutter:animate_do(flutter中的Animate.css)

简介 做过web开发的应该大部分人都知道Animate.css&#xff0c;它为开发者提供了一系列预定义的动画效果&#xff0c;可以通过简单的CSS类来实现各种动画效果。而animate_do 相当于flutter中的Animate.css,它提供了很多定义好的动画效果 基本使用 官方地址 https://pub-web.…

如何启用路由器dhcp?快解析如何内网穿透?

一、什么是DHCP&#xff1f; 动态主机设置协议&#xff08;DHCP&#xff09;是一种使网络管理员能够集中管理和自动分配 IP 网络地址的通信协议。在网络中&#xff0c;每个联网设备都需要分配独有的 IP 地址。并当有新计算机移到网络中的其它位置时&#xff0c;能自动收到新的…