《Linux从练气到飞升》No.28 Linux中的线程同步

🕺作者: 主页

我的专栏
C语言从0到1
探秘C++
数据结构从0到1
探秘Linux
菜鸟刷题集

😘欢迎关注:👍点赞🙌收藏✍️留言

🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

文章目录

    • 前言
    • 1 相关概念
      • 1.1 条件变量
      • 1.2 同步概念与竞态条件
      • 1.3 条件变量函数
    • 2 实际应用(见见猪跑
      • 2.1 模拟加锁未加条件变量(小迷给小芒煮饭且只有一个碗
      • 2.2 模拟加锁且加上条件变量
      • 2.3 模拟加锁且加条件变量(小迷给多个人做饭 只有一个碗
    • 3 条件变量关于等待接口的几个问题
      • 3.1 条件变量对的等待接口参数为什么需要互斥锁?
      • 3.2 pthread_cond_wait函数的实现原理
      • 3.3 线程等待的时候,被唤醒之后需要做什么事?

前言

当谈到多线程编程时,线程同步是一个至关重要的话题。在多线程环境中,我们需要确保不同线程之间的数据访问和操作能够正确、有序地进行,以避免出现竞争条件和数据不一致的情况。因此,线程同步成为了保障多线程程序正确性和可靠性的重要手段。

在本篇博客中,我将深入探讨线程同步的概念、原理和常用的同步机制,帮助读者更好地理解多线程编程中的挑战和解决方案。无论是初学者还是有一定经验的开发人员,都可以通过本文获得对线程同步的全面了解,并学习如何在实际项目中应用这些技术来确保多线程程序的稳定性和性能。

让我们一起深入研究线程同步,探索其中的奥秘,为多线程编程的世界增添一抹精彩的色彩。

1 相关概念

1.1 条件变量

  • 当一个线程互斥的访问某个变量时,它可能发现在其他线程改变状态之前,它什么也做不了
  • 例如一个线程访问队列时,发现队列为空,它只能等待,只到其他线程将一个节点添加到队列中,这种情况就需要用到条件变量

1.2 同步概念与竞态条件

  • 同步:在保证数据安全的前提下,让线程能够按照某种特定顺序访问临界资源,从而有效避免饥饿问题,这叫做同步。
  • 竞态条件:因为时序问题,而导致程序异常。我们称之为竞态条件。在线程场景下,这种问题也不难理解

1.3 条件变量函数

  1. 初始化
动态初始化:
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrictattr);🔄  ❓
参数:cond:要初始化的条件变量attr:NULL
静态初始化:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  1. 销毁
int pthread_cond_destroy(pthread_cond_t *cond)
  1. 等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:cond:要在这个条件变量上等待mutex:互斥量,之前的博客解释过
作用:谁调用该接口,就将谁放入PCB等待队列中
  1. 唤醒等待
唤醒PCB等待队列当中的所有线程:int pthread_cond_broadcast(pthread_cond_t *cond);
唤醒PCB等待队列当中至少一个线程:int pthread_cond_signal(pthread_cond_t *cond);

2 实际应用(见见猪跑

2.1 模拟加锁未加条件变量(小迷给小芒煮饭且只有一个碗

代码如下:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;int g_bowl=1;
pthread_mutex_t g_lock;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);g_bowl--;cout<<"I am "<<pthread_self()<<"I eat "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);g_bowl++;cout<<"I am "<<pthread_self()<<"I make "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);}return NULL;
}int main(){pthread_mutex_init(&g_lock,NULL);pthread_t tid_eat;pthread_t tid_make;int ret = pthread_create(&tid_eat,NULL,Eat,NULL);if(ret<0){cout<<"thread create failed"<<endl;}ret = pthread_create(&tid_make,NULL,MakeRice,NULL);if(ret < 0){cout<<"thread create failed"<<endl;}while(1){sleep(1);}pthread_mutex_destroy(&g_lock);return 0;
}

结果:
image.png
可以观察到bowl已经减为负数,这是因为小芒负责吃,当小芒拿到CPU的资源时,即使碗里面没有饭,它还是持续吃饭,最后居然出现了没有饭还能吃饭的情况,这显然是不合理的所以需要一个条件变量来控制能否吃,以及能否做

2.2 模拟加锁且加上条件变量

给小迷加上条件变量,bowl 里面有饭就不做饭,给小芒加上条件变量,bowl 没有饭就不吃饭。
代码如下:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;int g_bowl=1;
pthread_mutex_t g_lock;pthread_cond_t g_eat_cond;
pthread_cond_t g_make_cond;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);if(g_bowl<=0){pthread_cond_wait(&g_eat_cond,&g_lock);//等待小迷做好饭}g_bowl--;cout<<"I am "<<pthread_self()<<"I eat "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&g_make_cond);//通知小迷做饭}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);if(g_bowl>=1){pthread_cond_wait(&g_make_cond,&g_lock);//等待小芒吃饭 空出碗}g_bowl++;cout<<"I am "<<pthread_self()<<"I make "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&g_eat_cond);//通知小芒吃饭}return NULL;
}int main(){pthread_mutex_init(&g_lock,NULL);pthread_cond_init(&g_eat_cond,NULL);pthread_cond_init(&g_make_cond,NULL);pthread_t tid_eat;pthread_t tid_make;int ret = pthread_create(&tid_eat,NULL,Eat,NULL);if(ret<0){cout<<"thread create failed"<<endl;}ret = pthread_create(&tid_make,NULL,MakeRice,NULL);if(ret < 0){cout<<"thread create failed"<<endl;}while(1){sleep(1);}pthread_mutex_destroy(&g_lock);pthread_cond_destroy(&g_eat_cond);pthread_cond_destroy(&g_make_cond);return 0;
}

结果如下:
image.png

2.3 模拟加锁且加条件变量(小迷给多个人做饭 只有一个碗

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;int g_bowl=1;
pthread_mutex_t g_lock;pthread_cond_t g_eat_cond;
pthread_cond_t g_make_cond;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);if(g_bowl<=0){pthread_cond_wait(&g_eat_cond,&g_lock);}g_bowl--;cout<<"I am "<<pthread_self()<<"I eat "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&g_make_cond);}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);if(g_bowl>0){pthread_cond_wait(&g_make_cond,&g_lock);}g_bowl++;cout<<"I am "<<pthread_self()<<"I make "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&g_eat_cond);}return NULL;
}int main(){pthread_mutex_init(&g_lock,NULL);pthread_cond_init(&g_eat_cond,NULL);pthread_cond_init(&g_make_cond,NULL);pthread_t tid_make;int ret = pthread_create(&tid_make,NULL,MakeRice,NULL);if(ret < 0){cout<<"thread create failed"<<endl;}for(int i=0;i<3;++i){pthread_t tid_eat;int ret = pthread_create(&tid_eat,NULL,Eat,NULL);if(ret<0){cout<<"thread create failed"<<endl;}}while(1){sleep(1);}pthread_mutex_destroy(&g_lock);pthread_cond_destroy(&g_eat_cond);pthread_cond_destroy(&g_make_cond);return 0;
}

结果:
image.png
可以看到出现了负数的情况,这是为什么?
这是因为我们是使用if语句来判断条件的,可能线程刚好在这个时候进行了切换,导致多个eat线程拿到了锁,从而发生了这样的现象,想要解决这个问题只需要改为while语句即可

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;int g_bowl=1;
pthread_mutex_t g_lock;pthread_cond_t g_eat_cond;
pthread_cond_t g_make_cond;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);while(g_bowl<=0){pthread_cond_wait(&g_eat_cond,&g_lock);}g_bowl--;cout<<"I am "<<pthread_self()<<" I eat "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&g_make_cond);}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(&g_lock);while(g_bowl>0){pthread_cond_wait(&g_make_cond,&g_lock);}g_bowl++;cout<<"I am "<<pthread_self()<<" I make "<<g_bowl<<endl;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&g_eat_cond);}return NULL;
}int main(){pthread_mutex_init(&g_lock,NULL);pthread_cond_init(&g_eat_cond,NULL);pthread_cond_init(&g_make_cond,NULL);pthread_t tid_make;int ret = pthread_create(&tid_make,NULL,MakeRice,NULL);if(ret < 0){cout<<"thread create failed"<<endl;}for(int i=0;i<3;++i){pthread_t tid_eat;int ret = pthread_create(&tid_eat,NULL,Eat,NULL);if(ret<0){cout<<"thread create failed"<<endl;}}while(1){sleep(1);}pthread_mutex_destroy(&g_lock);pthread_cond_destroy(&g_eat_cond);pthread_cond_destroy(&g_make_cond);return 0;
}

结果:
image.png

3 条件变量关于等待接口的几个问题

3.1 条件变量对的等待接口参数为什么需要互斥锁?

pthread_cond_wait函数的内部,需要释放互斥锁。释放之后,其他线程就可以正常加锁操作了。
eg:就像之前小芒发现碗里面没有饭,则需要将自己放到PCB等待队列中,调用了pthread_cond_wait函数之后,需要将拿到互斥锁释放掉,小迷就可以访问到碗这个临界资源开始做饭。

3.2 pthread_cond_wait函数的实现原理

pthread_cond_wait函数内部,是先释放互斥锁,还是先将PCB放到等待队列中呢?
假设先释放互斥锁,此时可能做饭的小迷就已经将饭做好了,但是小芒还没有到等待队列中,小迷通知小芒吃饭,但是发现等待队列中为空,但是同时发现碗里面有饭,它就会将自己放入等待队列中等待,此时小芒也才将自己放入等待队列中,那么此时小迷和小芒就都在等待队列中进行等待,所以不能先释放互斥锁。

3.3 线程等待的时候,被唤醒之后需要做什么事?

  1. 移动出PCB等待队列
  2. 抢互斥锁
    1. 抢到了:pthread_cond_wait函数返回了
    2. 没抢到:pthread_cond_wait函数没有返回,等待抢锁

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

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

相关文章

陪玩系统圈子系统:打破孤单,寻找游戏共伴!APP小程序H5三端源码交付,支持二开!喜欢软件开发的朋友可以一起交流!

在如今快节奏的生活中&#xff0c;游戏作为一种娱乐方式&#xff0c;早已融入了人们的生活&#xff0c;成为许多人放松心情、释放压力的方式之一。然而&#xff0c;与此同时&#xff0c;游戏中的孤独感也成为一些玩家所面临的问题。为了解决这个问题&#xff0c;陪玩系统圈子系…

C++初阶:STL之vector类模板

目录 一.vector的介绍及使用 1.1.vector的介绍 1.2.vector的使用 1.2.1.vector的定义 1.2.2.vector iterator的使用 1.2.3.vector的空间增长问题 1.2.4.vector的增删查改 1.3.vector在OJ中的使用 题一&#xff1a;只出现一次的数字 题二&#xff1a;杨辉三角 题三&a…

【vue3】处理数组方法,在数组中获取指定条件所在的数组对象等持续更新笔记~~

1、在数组中获取指定条件所在的数组对象 &#xff08;1&#xff09;filter方法获取到的是包含指定项的数组 data.checkRow res.result.filter(item > item.checked 1);打印&#xff1a; &#xff08;2&#xff09;map方法取到的是包含指定项的数组&#xff0c;如果满足…

解决PP材质粘合问题用PP专用UV胶水

PP材料已经广泛应用于各行各业&#xff0c;在粘接中会有不同的问题需求&#xff0c;那么使用专用于PP的UV胶水可能是解决PP材质粘合问题的一种有效方法。 主要在于&#xff1a;UV胶水在紫外线照射下可以快速固化&#xff0c;形成坚固的连接。所以使用PP专用UV胶水时可以考虑&am…

基于亚马逊云科技新功能:Amazon SageMaker Canvas无代码机器学习—以构建货物的交付状态检测模型实战为例深度剖析以突显其特性

授权说明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在亚马逊云科技开发者社区、 知乎、自媒体平台、第三方开发者媒体等亚马逊云科技官方渠道。 目录 &#x1f680;一. Amazon SageMaker &#x1f50e;1.1 新功能发布&#xff1a;A…

基于PCIe的NVMe学习

一&#xff1a;基本概念 1.UltraScale&#xff1a;是Xilinx ZYNQ 系列产品 2.spec&#xff1a;大家现在别纠结于具体的命令&#xff0c;了解一下就好。老板交代干活的时候&#xff0c;再找spec一个一个看吧————猜测估计是命令表之类的。 3.TLP报文部分&#xff1a; 二&…

云桌面和桌面云是什么?中国桌面云市场谁在领跑?

当我们谈论云桌面和桌面云时&#xff0c;确实很容易产生混淆。它们都涉及到云计算技术在个人计算环境中的应用&#xff0c;但具体的应用场景和侧重点有所不同。 云桌面&#xff1a;虚拟化的桌面环境 云桌面是一种基于云计算的虚拟化技术&#xff0c;它允许用户通过互联网访问一…

Visual Studio开发环境的搭建

1.引言 Visual Studio是微软公司开发的一款强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它可以帮助开发人员进行各种编程任务&#xff0c;包括设计、开发、测试、调试和部署应用程序。典型功能包括&#xff1a; 代码编辑器。提供高效、智能的代码编辑器&#x…

Docker的私有仓库Harbor

Harbor概述 1.Harbor定义 私有&#xff0c;自定义用户的形式登陆仓库&#xff0c;拉取或者上传镜像。(内部管理的用户) Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户迅速搭建一个企业级的 Docker Registry 服务。 2.Docker Harbor…

【数学建模】《实战数学建模:例题与讲解》第十讲-时间序列预测(含Matlab代码)

【数学建模】《实战数学建模&#xff1a;例题与讲解》第十讲-时间序列预测&#xff08;含Matlab代码&#xff09; 基本概念移动平均&#xff08;Moving Average, MA&#xff09;:指数平滑法&#xff08;Exponential Smoothing&#xff09;:季节性调整&#xff08;Seasonal Adju…

SpringCloud系列(六)| 聊聊负载均衡

一、负载均衡概述 上一篇文章中&#xff0c;我们在集成OpenFeign的过程中提示我们需要加入了一个依赖就是&#xff1a; spring-cloud-starter-loadbalancer。 顾名思义&#xff0c;这个包的作用就是用来做负载均衡的。 简单解释一下什么是负载均衡&#xff0c;就是当我们的服…

Java8新特性:Lambda表达式

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

AI 搜索将如何影响 SEO

用户可以与之交互的 AI 搜索结果是肯定的。搜索营销人员要么对此感到生气&#xff0c;什么都不做&#xff0c;要么他们可以调查即将发生的事情并做好准备。 Google SGE 和 Bing 提供了相当相似的 AI 方法&#xff0c;是传统搜索和聊天机器人的混合体。 但这并不一定是当今 AI…

HarmonyOS--基础组件Text

Text组件 可以包含Span子组件。 接口 Text(content? : string | Resource) string: Text(我是ttttt) Resource: Text($r(app.string.aaaaaa)) 先找限定词目录&#xff0c;找不到内容 找base目录 属性 除支持通用属性外&#xff0c;还支持以下属性&#xff1a; 名称 参数…

全国合作商标服大赛决赛完整规则流程

本文是全国合作商标服大赛决赛完整规则流程&#xff0c;有需要的朋友可以参考下。 一、抢答比拼 1、政策管理考核题 系统评分。抢答题共15题/条线&#xff1a;单选10题&#xff0c;多选5题&#xff0c;基础分100分 单选答对10分/答错-5分&#xff0c;多选答对20分/答错-10分…

Mozilla 推出 Solo:借助 AI 帮助零编程用户创建网站

Mozilla 近日推出名为 Solo 的全新项目&#xff0c;面向没有任何编程经验的用户&#xff0c;通过融入 AI 能力&#xff0c;所创建的网站可以媲美专业开发者的开发效果。 Mozilla 表示该项目主要针对中小型企业、个体户&#xff0c;在官方演示中&#xff0c;用户只需要输入文本、…

SAP ABAP 使用cl_md_bp_maintain=>maintain更新BP税号CN0的数据,更新结果都会变成CN5类型问题处理

SAP ABAP 使用cl_md_bp_maintain>maintain更新BP税号CN0的数据&#xff0c;更新结果都会变成CN5类型&#xff0c;CN1类型一切正常。 1、BP税号 2、跟踪方法中代码 查看底层逻辑&#xff0c;发现CN0都被强制替换成CN5了&#xff0c;BP GUI界面还能正常使用CN0. 查询NOTES&a…

QT -CloudViewer工具

QT -CloudViewer工具 一、演示效果二、关键程序三、程序下载 一、演示效果 二、关键程序 void CloudViewer::doOpen(const QStringList& filePathList) {// Open point cloud file one by onefor (int i 0; i ! filePathList.size(); i) {timeStart(); // time startmycl…

Layui继续学习

1、简单评论区代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>社区评论区</title> <link rel"stylesheet" href"https://cdn.staticfile.org/layui/2.6.8/css/…

1+X大数据平台运维职业技能等级证书中级

hadoop&#xff1a; 由于我的功能限制&#xff0c;我无法直接为您执行这些操作或提供实际的截图。但我可以为您提供一步步的指导&#xff0c;帮助您完成这些任务。 1. 解压JDK安装包到“/usr/local/src”路径&#xff0c;并配置环境变量。 - 解压JDK&#xff1a;tar -zxf jd…