【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与互斥(线程三)
上一期我们进行了线程控制的了解与相关操作,但是仍旧有一些问题没有解决 本章第一阶段就是解决tid的问题,第二阶段是进行模拟一个简易线程库(为了加深对于C库封装linux原生线程的理解),第三阶段就是互斥。 目录 线程id…...
JavaEE—什么是服务器?以及Tomcat安装到如何集成到IDEA中?
目录 ▐ 前言 ▐ JavaEE是指什么? ▐ 什么是服务器? ▐ Tomcat安装教程 * 修改服务端口号 ▐ 将Tomcat集成到IDEA中 ▐ 测试 ▐ 结语 ▐ 前言 至此,这半年来我已经完成了JavaSE,Mysql数据库,以及Web前端知识的学习了&am…...
主流分布式消息中间件RabbitMQ、RocketMQ
分布式消息中间件在现代分布式系统中起着至关重要的作用。以下是一些主流的分布式消息中间件: 1. Apache Kafka - 特点:高吞吐量、低延迟、持久化、水平可扩展、分布式日志系统。 - 使用场景:日志收集与处理、实时流处理、事件驱动架构、大数…...
【Unity Linux】模型导致的Unity项目崩溃
模型需勾选Strip Bones。如不勾选,则开启项目崩溃。 也可以删除有问题模型的.meta文件。 (Unity默认会自动勾选,所以不会崩溃) 或打开.meta文件,将optimizeBones的值,由0改为1。(对应面板上的…...
22222
12212...
大数据领域的常用开发语言详解
大数据开发语言主要包括以下几个,以下是它们在大数据开发领域的优缺点和应用场景的详细说明: 1. Java 优点: 跨平台性:Java的“一次编写,到处运行”的特性使得其可以轻松地运行在多个操作系统上。面向对象ÿ…...
SpringBoot设置自动跳转前端界面
一般情况下,我们的Application启动文件的内容为一行的运行代码,默认启动项目以后不会自动跳转到我们的前端页面 public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);} 这里我的可以通过设置文件的内容&#…...
vue3前端解析大数据返给的数据格式
# xxx:111111111111111\n\n## 2222:\n- 99999999。\n- 564566556。\n- ", npm install marked import {marked} from markedmarked(# xxx:111111111111111\n\n## 2222:\n- 99999999。\n- 564566556。\n-)//就可以解析成 《…...
Incremental Player Build
*未解决,仅作记录 Unity 版本 2021.3.15f1 问题 Unity 发布webgl 平台卡在Incremental Player Build 界面。 解决 未找到明确原因,简化工程路径后发布成功。...
快钱支付股东全部股权已被质押!
根据近期工商信息,第三方支付机构快钱支付清算信息有限公司(简称“快钱支付”)实际控股方快钱金融服务(上海)有限公司(简称“快钱金融”),作为出质股权标的企业,被出质给…...
【鸿蒙学习笔记】数据类型
官方文档:ArkTS语言介绍 目录标题 声明变量声明常量数据类型 缺: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,提示特殊总帐标志类型“汇票和”预付定金“的特别总帐标志的过帐代码不能预制,这是系统写死的ÿ…...
鸿蒙 HarmonyOs 动画效果 快速入门
一、理论 1.1 animation属性 名称参数类型必填描述durationnumber否设置动画时长,默认值:1000,单位:毫秒temponumber否动画播放速度。数值越大,速度越快,默认为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:主动学习 图注意力变换器 变分自编码器,改善多标签图像分类 提出背景子解法1:多标签信息样本的选择子解法2:生成信息丰富且非冗余的合成样本 例子:胸部X射线图像分析传统方法的操作和局限GaNDLF方法的优势 工…...
Linux 权限介绍
文章目录 Linux 权限介绍权限类型权限的数字表示查看文件信息修改权限相关指令 Linux 权限介绍 在 Linux 系统中,权限管理是非常重要的一部分,它确保了系统的安全性和文件的合理访问。 权限类型 [ r ]代表可读(read)࿱…...
kernel header解析
一、kernel header定义: 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运行时遇到的问题
遇到的问题描述:我在绑定通道的时候发现了通道绑定失败, 原因: 在代码中我第一次创建交换机的时候类型的默认没有修改成topic类型的,导致后面的代码再去进行注册的时候并没有实现那个类型 解决: 更改代码࿰…...
EDI是什么?与ERP有何关系
EDI的发展过程 电子数据交换(Electronic Data Interchange,EDI)是一种通过电子方式传输商业文件的技术。EDI的历史可以追溯到20世纪60年代,当时企业开始使用计算机进行数据处理。最早的EDI系统是为解决大型企业间的信息交换问题而…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
