Linux线程:编织并发的梦幻世界

目录

🚩引言

🚩听故事,引概念

🚩生产者消费者模型

 🚀再次理解生产消费模型

 🚀挖掘特点

 🚩条件变量

🚀条件变量常用接口

 🚀条件变量的原理


🚩引言

上一篇博客,我们集中讨论了Linux线程互斥相关的概念,但随着互斥锁的使用,也容易产生线程饥饿问题,所以我们有必要学习一下Linux线程同步相关的概念。接下来我们开始学习。

🚩听故事,引概念

假设在学校有一个VIP学霸自习室,这个自习室非常的豪华,但是只有一张桌子,一次只能允许一个人去上自习,这个自习室的钥匙就在门口放着,谁离钥匙近,谁就获得钥匙,就可以在利用这个自习室自习。有一天,同学张三为了在自习室中学习,享受那种超棒的学习环境,他早上5点就爬了起来。所以,等到他来到自习室门口时,没有人。他非常高兴的取得了钥匙,开开门,然后把门一反锁,开始自习。一转眼上午了,张三感觉肚子饿了,但他不想放弃他早起得到的机会,于是,他就把门锁上了,然后随身带着钥匙,门口的同学看着他的行为都非常的愤怒,但是又无奈,谁让人家来的早呢!不一会儿,张三吃完饭回来了,开开门,继续上自习。这家伙有闷头学了3个小时,突然肚子痛,要上厕所,他又带着钥匙上厕所。上完之后继续学习。可能学的时间太长了,不想学了,有好几次都开门准备放回钥匙,但每次都想到:下次不知道什么时候才能再次在这里上自习了。他放回钥匙之际,此时,他是离要是最近的人,对钥匙的竞争能力最强,所以他又获得了钥匙即自习室的使用权。张三的行为错了吗?他没错,但是不合理

门外的同学看见他这么干都非常生气,决定把这个情况反映给值班的老师,老师认为这样就是不太好,决定结束自习的同学要想再次获得自习室的使用权,必须要排队。

张三照常把钥匙放了回去,正当他想伸手再次取得钥匙时,旁边的老师说:"去排队去"。张三无奈的去排队去了。心想:“卧槽,失算了,之前不是这样的规则呀”。


听完这个故事,有些问题我们来思考这样几个问题:

  1. 按照之前的规则,张三同学反复申请这间自习室的使用权,是建立这间自习室的初衷吗?不是。那对其他同学公平吗?不公平,有的同学可能在门口等了一天,都没有使用到这间自习室。
  2. 这间自习室的钥匙就像是一把互斥锁,自习室就像是临界资源,申请这间自习室的使用权的同学就像是不同的执行流。所以,也会发生有的执行流因为竞争能力太强,就像这个同学张三,总是可以获得这把锁,然后进行访问。但是这样就会造成其他的执行流因为竞争不到对应的锁,而处于线程饥饿状态。这种状态是不利于充分利用资源的。在我们之前的抢票的代码中,我们也发现总是那一个执行流在抢票,所以这个问题要解决。而解决方案就是Linux线程同步就是让执行流按照一定的顺序(不一定是绝对的顺序)来获得访问相关临界资源的权利。

🚩生产者消费者模型

 在现实生活中,我们学生就是典型的消费者,而生产者就是工厂。

假设,有一天我想吃火腿肠了,我就跑到一个加工火腿肠的工厂,对那里的工作人员说:“给我加工火腿肠,多少钱,我给你”。工作人员听了我说的话,一定以为我是个傻子。这是因为制作火腿肠需要机器,而打开机器需要成本的,一根火腿肠指定是远远不够的,今天我来了,买了一根火腿肠,给我现场加工了,明天别人来了,又只要一根。我相信这个工厂早晚得倒闭。

所以,在生产者和消费者之间,一定还存在超市这样一个交易场所。超市的作用是集中需求,分发产品,是对生产者生产的商品的临时保存。超市存在的另一个原因是:工厂一般远离消费者,超市的存在可以更加方便消费者消费。

消费者消费吃火腿肠的同时,生产者可能在放假;生产者在生产火腿肠的同时,消费者可能又没有在吃火腿肠。这种行为用计算机术语来形容就是:生产者和消费者实现了解耦。解耦就是互相不干扰的意思。超市在计算机体系中就是共享资源--->消费者需要通过访问超市来获取商品,生产者需要通过访问超市来销售商品。在超市中类似超市这样的作用的区域我们称之为缓冲区。

我们刚刚说:生产者消费者模型实现了生产和消费的解耦。那在我们写代码的过程中,有没有强耦合的代码呢?有的。

最显著的例子就是函数调用。我们认为的函数的过程一般是这个样子的:首先实参通过形参传递给函数,然后经过函数体内部复杂的运算,输出运输的结果。

  • 调用方:生产了数据。
  • 形参变量:暂时保存数据。‘
  • 目标函数:消费了数据。

假如,我们代码中main函数中调用的func函数。我们把数据传给func函数的时候,main函数在做什么?它什么都做不了,只能等待调用func函数结束。这就是强耦合关系的例子。 


 🚀再次理解生产消费模型

我们依旧使用上面的超市的模型来深度挖掘生产消费模型的特点。生产者对应一个或者多个线程。消费者对应一个或者多个线程。

①生产者和生产者之间是什么关系?

这个大家都知道,一定是互斥即竞争关系。俗话说同行是冤家,在超市展柜上展出自己的商品时,只能同一个品牌上完货,然后另一个火腿肠品牌再上货。负责生产数据的线程之间的关系也是如此。

②消费和消费者之间是什么关系呢?

试想一下:假如过几天就是世界末日,你和同学两个人去超市买火腿肠,但是火腿肠只有一根了,你们两个肯定因为这根火腿肠而吵起来。所以消费者和消费者之间同样是互斥关系

③生产者和消费者之间是什么关系呢?

假如有一次,你去超市买火腿肠,同时超市的工作人员或者火腿肠的厂家正在上货。你们两个此时比较尴尬,是先上货呢?还是先让我拿呢?是可以沟通的。但是对于计算机而言却不是这样的,操作系统内的线程确实无法沟通的。假如在操作系统内有一块空间,一个线程正在读取这块空间中的内容,与此同时,一个线程正在修改这块空间里的内容,毫无疑问读取的内容一定发生了改变。这是不合理的,所以生产者和消费者之间首先要保持着互斥的关系,不让其同时访问

假如有这么一个节日,节日期间流行吃火腿肠,所以超市里的供不应求。张三同学为了吃上火腿肠,每天准时准点来到超市问工作人员火腿肠到了没有,假如每天会有很多人前来询问,这对这位工作人员来说,也是一种负担。所以这位工作人员就要求加张三的维信,有火腿肠了就通知张三,这样张三就不用每天来到这里询问了。假如有这样一段时间,是火腿肠的淡季,超市里堆积了大量的火腿肠,但是工厂还源源不断的生产着,所以厂家每天来超市询问是否要进货。这也让超市的工作人员非常讨厌。一天,这位工作人员也加上了厂家的微信,如果需要进货,就给厂家发消息。所以这样,就间接维护了生产者和消费者的同步关系

总结起来,生产消费模型要遵守321的原则。只要我们想写生产者消费者模型,我们的本质是要维护321原则。

3种关系

生产者和生产者要保持互斥关系②消费者和消费者之间要保持互斥关系③消费者和生产者之间既要保持互斥关系也要保持同步关系。

2种角色

生产者线程,消费者线程

1个交易场所

一段特定结构的缓冲区 

 🚀挖掘特点

①生产线程和消费线程进行解耦

②支持生产者和消费者一段时间的忙闲不均的问题

有没有可能在一段时间内,生产者的生产能力很强,但是消费者的消费水平很低;或者生产者的生产水平很弱,但是消费者的消费水平很强。但是由于中间的超市的存在,可以平衡生产者和消费者之间生产和消费的问题。在我们的计算机内部,也是如此的

③提高效率

在社会发展中,出现了超市这样的事物,一定有它存在的道理和意义。假如没有超市的存在,我们需要购买商品时,就需要跑到工厂,相比于在超市中购买,肯定要浪费更多的时间。而工厂的工作人员就需要抽出人力来销售自己生产的商品,也浪费人力资源。所以生产者消费者模型的出现,可以提高我们的效率。在我们的计算机内部也是如此,同样适用。关于提高效率这块的内容,我们后边还会说明。


所以,我们现在是不是有能力将函数调用解耦呢?

是的,现在我们就可以实现函数调用解耦了。具体我们可以这样做:

我们先定义一个缓冲区,负责存储实参。我们先将要喂给调用函数作为实参的数据存储在缓冲区内,然后调用函数可以随时从缓冲区内读取数据,作为实参进行处理,然后输出结果。这样两个执行流就由串行执行变为并发执行,真正意义上实现了解耦。


但我们忽略了一个问题:生产者和消费者的关系是互斥之间的关系,就是同一时间,仅允生产线程和消费线程中的一个线程访问缓冲区(也就是临界区)。假如生产者的优先级非常高,同时缓冲区的数据已满,不允许再写入数据,但是生产线程却不断的进行查询,这样也就会导致一个线程一直访问临界资源,就会造成我们刚刚说的自习室问题。具体如图:

如上图,结合这份伪代码,大家应该可以理解。这时,我们就要提出条件变量的概念了。

 🚩条件变量

当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。这句话应该如何理解呢?

如上图,若干个线程互斥性的访问ticket这个变量,但只有剩余票数大于0时,才能完成抢票的动作,否则什么也干不了。此时如果一个线程的优先级很高,那么它就会不停的查询ticket的值,造成一定程度上的无用查询。这时我们就可以设定一个条件变量,等到ticket大于0时,通知该线程来抢票,不用一直在这里查询了。这就是条件变量的用处。

🚀条件变量常用接口

// 所有条件变量的相关函数都在该头文件下
#include <pthread.h>
// 创建一个条件变量
pthread_cond_t +变量名
// 销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
// 对一个条件变量进行初始化,参数:cond:要初始化的条件,attr:NULL
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
// 如果这个条件变量是静态的或者全局的,也可以这样初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 等待条件满足,参数:cond:要在这个条件变量上等待,mutex:互斥量,后面详细解释
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
// 如下两个函数是唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

注意:这些函数如果有返回值,默认成功的话,返回0;失败的话,错误码被设置。

接下来,见见猪跑

#include <stdio.h>
#include <stdlib.h> 
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *r1(void *arg)
{
    while (1)
    {
        pthread_cond_wait(&cond, &mutex);
        printf("活动\n");
    }
}
void *r2(void *arg)
{
    while (1)
    {
        pthread_cond_signal(&cond);
        sleep(1);
    }
}
int main(void)
{
    pthread_t t1, t2;
    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&t1, NULL, r1, NULL);
    pthread_create(&t2, NULL, r2, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
}

 🚀条件变量的原理

接下来,我们讲一个故事

一到招聘季,互联网公司总是派面试官以出差的形式去全国各地招收优秀的人才。他们每到一处地方,一般都会包下一个宾馆的一层楼,作为他们的面试场所。假设他们来到了济南,然后把面试场所安排在了万达酒店。

在一间屋子里,面试官正面试着一位同学,不一会儿面完了。此时门口挤满了求职者,他们都高举着自己的简历,然后高喊:"我先来的,应该先面我"。无奈,面试官只能挑喊的声音大的先面试。等到下午,吸取了经验,面试官在门口划定了一块区域,说:“只从这里选人面试,并且排队”

 

 其中,这块区域就像条件变量。

当条件不满足时,我们线程必须去某些定义好的条件变量下等待。

说到这里,本篇内容就结束了,我们下期博客再见!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习LLM的随笔

1、信息量、信息熵、交叉熵和困惑度 &#xff08;1&#xff09;信息熵&#xff1a;信息熵中使用 l o g 2 ( p ( x ) ) log_2(p(x)) log2​(p(x)) 来表示对 x x x 编码需要的编码长度。由于不同事件发生的概率不同&#xff0c;我们不能简单地将这些信息量相加&#xff0c;而应…

Day01-02-gitlab

Day01-02-gitlab 1. 什么是gitlab2. Gitlab vs Github/Gitee3. Gitlab 应用场景4. 架构5. Gitlab 快速上手指南5.0 安装要求5.1 安装Gitlab组件5.3 配置访问url5.6 初始化5.8 登录与查看5.9 汉化5.10 设置密码5.11 目录结构5.12 删除5.13 500 vs 5025.14 重置密码 6. Gitlab用户…

springboot美术馆售票管理系统-计算机毕业设计源码17485

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 2.2.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设…

PMP报考条件是什么?很多人都没读懂...

最近正值8月份考试报名期&#xff0c;想计划考8月份考试的宝子可以准备起来了&#xff0c;下面是报名时间和考试安排 8月考试时间安排&#xff1a; &#x1f449;报名时间在7.9日—12日 &#x1f449;考试时间在8.31日&#xff08;周六&#xff09; 一、PMP报名条件是什么&am…

vue3中 slot使用

默认插槽&#xff1a; 这是最基本的插槽类型&#xff0c;当没有指定 name 属性时&#xff0c;插槽是默认插槽。 子组件&#xff1a; <template><div class"child"><h2>子组件内容</h2><slot></slot> <!-- 默认插槽&#x…

反射快速入门

反射就是通过字节码文件获取类的成员变量、构造方法和成员方法的所有信息。 利用反射&#xff0c;我们可以获取成员变量的修饰符、名字、类型、取值。我们可以获取构造方法的名字、形参&#xff0c;并利用通过反射获取的构造方法创建对象。我们可以获取成员方法的修饰符、名字、…

LeetCode 子集

原题链接78. 子集 - 力扣&#xff08;LeetCode&#xff09; 这是一道暴力搜索问题参考大佬们的题解&#xff0c;对这类题目做出一下总结 1.确定递归参数变量 2.递归结束条件 3.做出选择&#xff0c;递归调用进入下一层 4.回溯&#xff0c;返回到递归前的状态 要完成前面这…

2024Datawhale-AI夏令营——机器学习挑战赛——学习笔记

#ai夏令营#datawhale#夏令营 Day1:入门级demo运行 这个其实比较简单&#xff0c;按照操作来做就行了&#xff0c;特征工程和调参暂时都没有做&#xff0c;后续的才是重头戏。

你想活出怎样的人生?

hi~好久不见&#xff0c;距离上次发文隔了有段时间了&#xff0c;这段时间&#xff0c;我是裸辞去感受了一下前端市场的水深火热&#xff0c;那么这次咱们不聊技术&#xff0c;就说一说最近这段时间的经历和一些感触吧。 先说一下自己的个人情况&#xff0c;目前做前端四年&am…

时钟服务器方案选型推荐:ATGM332D-5T和ATGM331C-5T

ATGM331C-5T系列模块同样是具有高灵敏度、低功耗、低成本等优势&#xff0c;适用于电力授时设备、时钟服务器、守时设备&#xff0c;可以直接替换Ublox LEA T系列模块。 性能指标&#xff1a; 从下面的图来看&#xff0c;ATGM331C-5T系列比ATGM332D-5T系列性能更好&#xff0c;…

大模型对汽车行业意味着什么?_汽车企业大模型

引 言 大模型是一种利用海量数据进行训练的深度神经网络模型&#xff0c;其特点是拥有庞大的参数规模和复杂的计算结构。通过在大规模数据集上进行训练&#xff0c;大模型能够学习到丰富的模式和特征&#xff0c;从而具备强大的泛化能力&#xff0c;可以对未知数据做出准确的预…

轻松省电!教你苹果手机自动调节亮度怎么设置

在日常使用手机的过程中&#xff0c;屏幕亮度是影响电池续航的关键因素之一。苹果手机提供的自动调节亮度功能&#xff0c;可以根据环境光线自动调整屏幕亮度&#xff0c;从而提供最佳的视觉体验并有效延长电池使用时间。想知道苹果手机自动调节亮度怎么设置吗&#xff1f; 本…

如何使用巴比达免费内网穿透服务

#灵感# 访问内部网络资源变得越来越重要。无论是远程办公、远程监控设备还是远程管理服务器&#xff0c;内网穿透技术都提供了一种安全、便捷的解决方案。巴比达&#xff08;Babada&#xff09;作为一种免费的内网穿透服务&#xff0c;为用户提供了快速、安全地访问其内部网络中…

AI人才争夺战:巨头眼中的产品经理必备技能

前言 在人工智能的浪潮下&#xff0c;BAT等一线互联网企业纷纷加码布局&#xff0c;对AI领域的人才需求空前高涨。然而&#xff0c;要在众多求职者中脱颖而出&#xff0c;成为企业眼中的人才&#xff0c;不仅需要深厚的产品功底&#xff0c;更要具备对AI的深刻理解和应用能力。…

JAVASE进阶day03(lamda表达式 ,内部类)

内部类 1.内部类的基本使用 package com.lu.day03;public class Student {private int b 12;public class A{private int b 11;public void show(){int b 10;System.out.println("我是A");System.out.println(b);System.out.println(this.b);System.out.println(…

开源即正义,3D软件Blender设计指南

在当今数字化时代&#xff0c;开源软件的崛起不仅代表着技术的发展&#xff0c;更象征着一种信息自由和技术民主的理念。其本质是集众人之智&#xff0c;共同去完善一个软件&#xff0c;最终使双方互惠共赢。具体来说&#xff0c;开源的价值&#xff0c;在于打破资源垄断&#…

PIN对PIN替代T J A 1 0 2 8,LIN芯片

内部集成高压 LDO&#xff08;耐压 40V&#xff09; 稳压源&#xff0c;可为外部 ECU微控制器或相关外设提供稳定的 5V/3.3V 电源&#xff0c;输出电流可达70mA&#xff1b; 可在 5.5V ~ 28V 电压范围内工作&#xff0c;支持 12V 应用。在休眠模式下可实现极低电流消耗&#x…

Java集合整理笔记

目录 1.集合基础概念 1.1 集合 1.2 单例集合 1.2.1 List系列 1、ArrayList 2、LinkedList 3、Voctor​编辑 1.2.2 Set系列 1、HashSet 集合 2、LinkedHashSet 集合 3、TreeSet集合 1.3 双例集合 1.3.1 HashMap 1.3.2 LinkedHashMap 1.3.3 TreeMap 1.4 快速失败…

python pdfplumber优化表格提取

样例pdf 直接使用文本提取效果&#xff1a; 使用表格提取 根据提取的文本信息是没办法获取到表格数据的&#xff0c;太乱了。尤其是 3 4列。 解决&#xff1a; 自行画线&#xff0c;根据画线进行提取。 效果&#xff1a; 思路&#xff1a; 1.根据表头进行画竖线 2.根据行坐…

thinksboard 网络请求方式

html模块 deleteImage点击事件 <div class"tb-images tb-absolute-fill" [ngClass]"{tb-dialog-mode: dialogMode, mat-padding: !dialogMode}"><div fxFlex fxLayout"column" class"tb-images-content" [ngClass]"{t…