当前位置: 首页 > news >正文

【Linux】线程id与互斥(线程三)

上一期我们进行了线程控制的了解与相关操作,但是仍旧有一些问题没有解决在这里插入图片描述
本章第一阶段就是解决tid的问题,第二阶段是进行模拟一个简易线程库(为了加深对于C++库封装linux原生线程的理解),第三阶段就是互斥。

目录

  • 线程id:
    • LWP与tid:
    • 动态库的加载:
    • 线程id:
    • 如何理解维护在库中:
    • 再次感受一下pthread_join():
    • 线程局部存储:
  • 封装线程库:
    • 封装:
  • 互斥:

线程id:

LWP与tid:

我们还是先来写一段简单的代码进行验证一下LWP与线程id的关系。

代码:
在这里插入图片描述
验证结果:在这里插入图片描述

在这里插入图片描述
足以观察到LWP与tid的差距是非常大的。

这说明给用户提供的线程id并不是内核中的LWP,而是自己维护的一个唯一值。
自己就是pthread库。

虽然刚开始觉得不符合常理,但仔细想一想而本该如此:
因为linux并没有线程,但是我们用户需要线程的概念,所以pthread库充当了一个中间角色,封装linux中的轻量级进程,因此,并不需要呈现给用户LWP的值,给用户呈现自己封装的线程id即可。就像C语言中的FILE,我们直接用库封装好的,并不需要在使用文件描述符fd了,也不需要展现给用户。

因为库提供了线程id,所以库也要对pthread进行管理,怎么理解呢?
可以理解为学校给你提供了学号,所以学校要对你进行管理。

我们的linux肯定提供了轻量级进程的调度系统调用,但是一个线程不仅仅需要被调度,也需要一个id,栈大小,被谁启动…这些属性也是由库做管理的!

针对管理我们要展开一下。

动态库的加载:

那就要先看一下线程库的加载,首先动态库和我们的程序肯定都是在磁盘上的文件。
在这里插入图片描述

当我们./运行时,会建立内核数据结构 + 加载数据与代码。

在这里插入图片描述
当我们执行到pthread_create时,因为我们还未加载动态库,会触发缺页中断,去加载动态库,再将动态库映射到共享区。
在这里插入图片描述
此时我们就可以正常去执行我们的pthread_create去创建线程了。

而我们也说过库需要对我们的线程id,栈的大小…进行维护,也就是进行管理

而管理就需要对该对象进行描述再进行组织,下图就是描述他的结构体pthread_t。在这里插入图片描述
其中的struct pthread是用户最基本的线程属性,线程局部存储我们稍后再来进行讲解。而编程栈就是我们常说的每个线程都有一个独立的线程栈!

组织我们可以看成是使用一个数组进行组织起来的。

线程id:

所以以后想找线程属性,拥有地址即可进行管理,而我们的tid就是相应的pthread_t的地址。

如何理解维护在库中:

我们还是以FILE进行举例。
我们的FILE是一个结构体:

struct FILE
{int fd;char buff[N];...
}

我们打开一个文件会得到一个FILE的指针。

而这个FILE结构体指针就维护在标准库中,进入这个函数时,会执行malloc(sizeof(struct FILE))类似的代码在堆上申请空间,等执行完之后返回给用户FILE*,让用户进行操控。所以我们也就可以理解维护在库中了。

就像我们使用STL中的各种容器,不需要管底层是如何扩容的。

再次感受一下pthread_join():

在这里插入图片描述
所以我们也就理解了pthread_create时的attr在这里插入图片描述
这就是用来控制pthread_t的属性,比如控制栈的大小…

总结:linux线程 = pthread库 + LWP,其中内核维护的LWP与动态库中维护的线程是1:1的。

但是同学们,我们该如何保障新线程轻量级进程会使用你指定的栈?
因为我们的轻量级进程中有一个系统调用:clone。
在这里插入图片描述
第一个是回调函数,第二个就是指定的栈,第三个是参数,所以pthread库本质就是对这样的一堆系统调用进行封装。

线程局部存储:

现在还剩最后一个问题,线程局部存储是啥?在这里插入图片描述
我们先来看这样一段代码:
搞一个全局变量,新线程改,主线程读取。
在这里插入图片描述

现象: 一改具改,符合预期。

但是如果我们想要互相不影响,也就是新主线程虽然用同一个全局变量名字,但是实际却是两个地址。
我们可以加入编译选项__thread
注意:这个只可以修饰全局的内置类型。

在这里插入图片描述

现象:可以看到虚拟地址也不相同。
在这里插入图片描述
结论:虽然看起来还是用一个,但实际上各自私有一份

封装线程库:

本质是为了更好的理解C++11是如何进行封装的。

我们的目标是实现如下几个接口:
在这里插入图片描述
也可以在来一个GetStatus,调用可以观察到此线程是否正在运行。

封装:

在这里插入图片描述
先来解释一下成员变量,_name就是线程名字,_tid是线程id,_func是用户传递来的要执行的函数,_isRunning是代表当前线程是否在运行。


在实现时有两个的坑点。
其一:因为我们是进行封装,所以是在类中实现。
但是我们将routine写出了之后却找不到对应routine,这是因为类中的成员默认有隐含的this指针,所以参数的个数就不匹配了。
在这里插入图片描述
解决这种问题的办法很多,但我们最喜欢用static进行修饰,这样就不会有this指针了,这个函数属于整个类。
在这里插入图片描述
可是这样我们就无法访问类中的成员变量了,因为没有this指针~

那怎么办?
把this指针当做参数传给routine!

在这里插入图片描述
这样就可以调用外部给我们传的指定函数了。
可是这样调用未免有些丑陋。
在这里插入图片描述
在将stop,join进行填补即可,注意,我们的构造函数需要一个你指定的名字和待执行函数。

主函数代码:
在这里插入图片描述
现象:

在这里插入图片描述
也是完成了我们预期的工作。

源代码:

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstdio>namespace cyc
{class mythread{public:typedef void (*func_t)(std::string);mythread(const std::string &name, func_t func) : _name(name), _func(func), _isRunning(false){}~mythread(){}void Excute(){_isRunning = true;_func(_name);_isRunning = false;}static void *routine(void *arg){mythread *self = static_cast<mythread *>(arg);self->Excute();return nullptr;}void Start(){int n = ::pthread_create(&_tid, nullptr, routine, (void *)this);if (n != 0){perror("pthread_create fail");exit(1);}}void Stop(){if (_isRunning){pthread_cancel(_tid);_isRunning = false;}}void Join(){int n = pthread_join(_tid, nullptr);if (n != 0){perror("pthread_join fail");exit(1);}}std::string GetStatus(){if(_isRunning) return "Running";else return "sleeping";}private:std::string _name;pthread_t _tid;func_t _func;bool _isRunning;};
}

于是我们就完成了一个很简易的封装~

互斥:

多个线程能够看到的资源叫做共享资源。
但是也会有一些问题,比如我们线程通信,一个线程想写hello world,但是刚写了hello就被另一个进程独走了,叫做读写不一致,
所以我们需要对共享资源做保护。

其中最简单的方法是互斥。

但是我们总要先见一见吧。
我们模拟一个抢票的代码,假设一共有1w张票,创建4哥线程同时去抢,每抢一次记录一下抢之前的票数,当票数<=0就是出现了问题。

我们就是用刚刚模拟实现的线程进行操作。
代码:
在这里插入图片描述
现象:
在这里插入图片描述
果然出现了0甚至负数,这就证明我们的抢票提供非常的失败~

可是原理是什么呢?

首先我们要有两个储备知识。
其一是判断也是一种运算,为逻辑运算。
一共有两种运算,分别为算术运算与逻辑运算,简称算罗运算。

其二是线程的切换,CPU内寄存器只有一套,但是拥有的数据有多套。切换时带走自己的数据,回来时会回复!

对于1来说,逻辑判断至少分为3步,虽然语法上表现为3步,但是实际转换到汇编有2-3步。在这里插入图片描述
我们假设线程1在还有最后一张票时进入,已经判断完毕了,但是此时tickets还没进行--,这时时间片到了,线程1被切换,带走了当前寄存器的值与记录执行到的语句。同理2,3,4也都分别执行完判断语句就被切换这就有很大的问题了。

因为只有一张票却有4个线程进入了,等线程a,b,c,d分别恢复时将数据又放回到寄存器中。
注意:进行--时需要将数据重读,修改,在放回内存中这三步

所以票数就变为0,-1,-2…

下章继续~

相关文章:

【Linux】线程id与互斥(线程三)

上一期我们进行了线程控制的了解与相关操作&#xff0c;但是仍旧有一些问题没有解决 本章第一阶段就是解决tid的问题&#xff0c;第二阶段是进行模拟一个简易线程库&#xff08;为了加深对于C库封装linux原生线程的理解&#xff09;&#xff0c;第三阶段就是互斥。 目录 线程id…...

JavaEE—什么是服务器?以及Tomcat安装到如何集成到IDEA中?

目录 ▐ 前言 ▐ JavaEE是指什么? ▐ 什么是服务器&#xff1f; ▐ Tomcat安装教程 * 修改服务端口号 ▐ 将Tomcat集成到IDEA中 ▐ 测试 ▐ 结语 ▐ 前言 至此&#xff0c;这半年来我已经完成了JavaSE&#xff0c;Mysql数据库&#xff0c;以及Web前端知识的学习了&am…...

主流分布式消息中间件RabbitMQ、RocketMQ

分布式消息中间件在现代分布式系统中起着至关重要的作用。以下是一些主流的分布式消息中间件&#xff1a; 1. Apache Kafka - 特点&#xff1a;高吞吐量、低延迟、持久化、水平可扩展、分布式日志系统。 - 使用场景&#xff1a;日志收集与处理、实时流处理、事件驱动架构、大数…...

【Unity Linux】模型导致的Unity项目崩溃

模型需勾选Strip Bones。如不勾选&#xff0c;则开启项目崩溃。 也可以删除有问题模型的.meta文件。 &#xff08;Unity默认会自动勾选&#xff0c;所以不会崩溃&#xff09; 或打开.meta文件&#xff0c;将optimizeBones的值&#xff0c;由0改为1。&#xff08;对应面板上的…...

22222

12212...

大数据领域的常用开发语言详解

大数据开发语言主要包括以下几个&#xff0c;以下是它们在大数据开发领域的优缺点和应用场景的详细说明&#xff1a; 1. Java 优点&#xff1a; 跨平台性&#xff1a;Java的“一次编写&#xff0c;到处运行”的特性使得其可以轻松地运行在多个操作系统上。面向对象&#xff…...

SpringBoot设置自动跳转前端界面

一般情况下&#xff0c;我们的Application启动文件的内容为一行的运行代码&#xff0c;默认启动项目以后不会自动跳转到我们的前端页面 public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);} 这里我的可以通过设置文件的内容&#…...

vue3前端解析大数据返给的数据格式

# xxx&#xff1a;111111111111111\n\n## 2222&#xff1a;\n- 99999999。\n- 564566556。\n- ", npm install marked import {marked} from markedmarked(# xxx&#xff1a;111111111111111\n\n## 2222&#xff1a;\n- 99999999。\n- 564566556。\n-)//就可以解析成 《…...

Incremental Player Build

*未解决&#xff0c;仅作记录 Unity 版本 2021.3.15f1 问题 Unity 发布webgl 平台卡在Incremental Player Build 界面。 解决 未找到明确原因&#xff0c;简化工程路径后发布成功。...

快钱支付股东全部股权已被质押!

根据近期工商信息&#xff0c;第三方支付机构快钱支付清算信息有限公司&#xff08;简称“快钱支付”&#xff09;实际控股方快钱金融服务&#xff08;上海&#xff09;有限公司&#xff08;简称“快钱金融”&#xff09;&#xff0c;作为出质股权标的企业&#xff0c;被出质给…...

【鸿蒙学习笔记】数据类型

官方文档&#xff1a;ArkTS语言介绍 目录标题 声明变量声明常量数据类型 缺&#xff1a;byte charNumber类型 short int long float doubleBoolean类型 booleanString类型Void类型Object类型Array类型Enum类型Union类型Aliases类型 [代码总结] 声明变量 let hi: string hel…...

SAP实现特别总账的凭证预制

SAP实现特别总账的凭证预制 仔细理解只有”其他”的特殊总帐标识才可预制凭证这句话. F-29/f-48不可预制。F-29/f-48预制时出现错误消息号 FP 030&#xff0c;提示特殊总帐标志类型“汇票和”预付定金“的特别总帐标志的过帐代码不能预制&#xff0c;这是系统写死的&#xff…...

鸿蒙 HarmonyOs 动画效果 快速入门

一、理论 1.1 animation属性 名称参数类型必填描述durationnumber否设置动画时长&#xff0c;默认值&#xff1a;1000&#xff0c;单位&#xff1a;毫秒temponumber否动画播放速度。数值越大&#xff0c;速度越快&#xff0c;默认为1curvestring | Curve否 设置动画曲线。 默…...

PyTorch学习之 torch.squeeze 函数

PyTorch学习之 torch.squeeze 函数 一、功能 torch.squeeze 的主要作用是从给定的张量 input 中移除所有尺寸为1的维度。 二、基本语法 torch.squeeze(input, dimNone)三、参数说明 input (Tensor): 输入的张量。dim (int, 可选): 指定要移除的尺寸为1的维度 如果未指定&am…...

达梦数据库系列—17. 主备集群搭建-实时主备

目录 配置实时主备 1、环境说明 2、数据准备 脱机备份、脱机还原方式 联机备份、脱机还原方式 3、配置主库 3.1 配置 dm.ini 3.2 配置 dmmal.ini 3.3 配置 dmarch.ini 3.4 配置 dmwatcher.ini 3.5 启动主库为mount 3.6 设置 OGUID 3.7 修改数据库模式 4、配置备库…...

【24医学顶刊】GANDALF:主动学习 + 图注意力变换器 + 变分自编码器,改善多标签图像分类

GANDALF&#xff1a;主动学习 图注意力变换器 变分自编码器&#xff0c;改善多标签图像分类 提出背景子解法1&#xff1a;多标签信息样本的选择子解法2&#xff1a;生成信息丰富且非冗余的合成样本 例子&#xff1a;胸部X射线图像分析传统方法的操作和局限GaNDLF方法的优势 工…...

Linux 权限介绍

文章目录 Linux 权限介绍权限类型权限的数字表示查看文件信息修改权限相关指令 Linux 权限介绍 在 Linux 系统中&#xff0c;权限管理是非常重要的一部分&#xff0c;它确保了系统的安全性和文件的合理访问。 权限类型 [ r ]代表可读&#xff08;read&#xff09;&#xff1…...

kernel header解析

一、kernel header定义&#xff1a; aarch64 kernel header u32 code0; /* Executable code */ u32 code1; /* Executable code */ u64 text_offset; /* Image load offset, little endian */ u64 image_size; /* Effective Image size, little…...

MQ运行时遇到的问题

遇到的问题描述&#xff1a;我在绑定通道的时候发现了通道绑定失败&#xff0c; 原因&#xff1a; 在代码中我第一次创建交换机的时候类型的默认没有修改成topic类型的&#xff0c;导致后面的代码再去进行注册的时候并没有实现那个类型 解决&#xff1a; 更改代码&#xff0…...

EDI是什么?与ERP有何关系

EDI的发展过程 电子数据交换&#xff08;Electronic Data Interchange&#xff0c;EDI&#xff09;是一种通过电子方式传输商业文件的技术。EDI的历史可以追溯到20世纪60年代&#xff0c;当时企业开始使用计算机进行数据处理。最早的EDI系统是为解决大型企业间的信息交换问题而…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...