【Linux】从零开始认识多线程 --- 线程ID

在这里插入图片描述

在这个浮躁的时代
只有自律的人才能脱颖而出
-- 《觉醒年代》

1 前言

上一篇文章中讲解了线程控制的基本接口:

  1. 线程创建pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);:

    • pthread_t *thread :输出型参数,线程ID。
    • const pthread_attr_t *attr :线程属性(优先级,上下文…),默认传入nullptr
    • void *(*start_routine) (void *) : 函数指针,线程需要执行的函数地址。
    • void arg:想要传入到线程的信息,可以传入int,string地址或者传入一个类对象的地址。
  2. 线程等待pthread_join(pthread_t thread, void **retval);

    • pthread_t thread:需要进行等待的线程ID
    • void **retval: 获取的返回信息
  3. 线程终止pthread_cancel(pthread_t thread)

    • pthread_t thread:需要进行终止的线程ID

需要深入理解的是线程传参!可以传递任何类型!注意传递堆空间的地址,保持线程独立性!

通过创建的线程我们看到了线程的tid,它和LWP不一样,是类似一个地址的存在,今天我们来详细分析一下线程ID。而且线程是有独立的栈空间的,这个独立栈空间是在哪里的?与线程ID有没有关系?

2 理解线程库与线程ID

我们先来写一个基础的创建线程的程序:

#include <iostream>
#include <pthread.h>
#include <string>
#include<unistd.h>std::string ToHex(int x)
{char buffer[128];snprintf(buffer , 128 , "0x%x" , x);return buffer;
}void *threadrun(void *args)
{std::string name = static_cast<const char *>(args);while (true){std::cout << "name: " << name << "is running... ,tid: " << ToHex(pthread_self()) << std::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadrun, (void *)"thread-1");std::cout << "new thread is running ,tid: " << tid << std::endl;pthread_join(tid, nullptr);return 0;
}

我们运行可以看到tid
在这里插入图片描述
也可以通过ps -aL来查看查看运行线程的信息:
在这里插入图片描述
显然tidLWP是不一样的,用户能获取的线程ID不是内核中的LWP,而内核中的LWP其实也不需要给用户呈现,tid是一个库内部自己维护的唯一值,因为库内部需要承担对线程的管理维护。

我们来深入理解库。
首先,库没被加载的时候是在磁盘中的,而线程库是一个动态库,本质上是一个文件。可执行程序也是在磁盘中。刚开始运行的时候,可执行程序会先变成一个进程,加载代码和数据到内存中,并同步创建PCB,页表,地址空间…当CPU调度到这个进程,会运行代码,进而动态创建多线程!

接下来,运行到创建线程的代码的前提是把库加载到进程的地址空间中!动态库也要加载到内存中。进程要使用动态库需要将动态库映射到进程地址空间的共享区中!代码运行到动态库中的代码时会跳转到共享区对应函数,在通过其偏移量映射到内存中库函数实现部分,完成动态库函数的调用!
在这里插入图片描述
LInux内核只会维护轻量级进程,通过LWP(轻量级进程ID)维护,而用户层看到的是线程,需要的是线程的ID,线程的相关属性。上面我们提过线程是线程库维护的!接下来我们来看看线程库内部是如何维护管理的?如果库要管理线程和内核管理进程类似!同样遵循先描述,再组织

  1. 动态库内部会有一个描述线程属性的内存块,每一个线程都会创建这样一个内存块结构,用来描述属性。
  2. 这个内存块内部有线程在用户层面的基本属性,线程的独立栈结构

在这里插入图片描述
也就是在库中创建了描述线程的相关结构体字段属性,因为是连续开辟的,所以管理方法类似数组。未来我们只要找到线程控制的地址即可!pthread_t id就是一个地址!通过这个地址我们就可以访问这个内存块的所有属性!
我们对比一下FILE* fopen()这个接口会返回一个文件指针(而不是一个文件对象),那么这个指针指向的文件是在哪里呢?在C标准库中!返回的也是一个地址(指针)。这和创建线程是一致的!@
注意一点:在库里维护不一定要在库里开辟空间,都可以进行malloc的!

再来看pthread_join,如何理解?因为在库内部中线程结束时会直接return,并没有进行资源的释放,所以如果不进行join就会产生内存泄漏!join就是通过tid来找到对应位置来释放资源!

一个线程内部就可以有一个数组来维护一个栈结构!线程就独立的拥有自己的栈结构了!栈空间本质是地址空间的一部分区域!主线程使用自己的栈,新线程使用自己开劈的栈!

在用户层面是线程,内核层是轻量级进程,他们是1 :1的。

  1. lwp是用来调度的单位:具有自己的系统调用,pthread库就是对这些系统调用的封装!
  2. 线程概念的表现是在用户层的!
    在这里插入图片描述

Linux的线程 = pthread库中线程的属性集 + LWP

总的来说,pthread_t tid就是线程属性集合的起始虚拟地址 — 在pthread中进行维护。

3 线程局部存储

接下来我们,谈一下线程的局部存储。
我们设置一个全局变量gval,让新线程和主线程都不断打印,但是只有新线程进行++工作:
在这里插入图片描述
可以看到他们的地址是一致的,因为他们共享地址空间,会访问到同一个gval!

今天如果我们不想让他们看到同一个gval呢?让他们各自拥有一份,我们可以使用编译选项__thread(只在linux有效,只能修饰内置类型)

__thread int gval = 100;

在这里插入图片描述
可以看到此时主线程和新线程就是不一样的gval了!地址也不一样,说明新线程和主线程是各自拥有一份gval

这个全局变量,在加上__thread后就会在线程局部存储中各自储存一份,互不干扰!

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

43 华三AC登录Web页面

一 无线上WEB页面 1 创建vlan 56 [AC-KongZhi]vlan 56 2 退出 [AC-KongZhi-vlan56]quit 3 进入vlan三层口 配置IP地址 [AC-KongZhi]interface Vlan-interface 56 [AC-KongZhi-Vlan-interface56]ip address 192.168.56.55 24 4 在AC控制器与Host主机的接口上能通关vlan 5…

MySQL进阶之(十)事务和隔离级别

十、事务和隔离级别 10.1 事务10.1.1 事务介绍10.1.2 事务四大特性10.1.3 事务的状态10.1.4 如何使用事务01、事务控制语句02、事务操作 10.2 事务的隔离级别10.2.1 事务数据可见性和并发问题01、脏写&#xff08;Dirty Write&#xff09;/更新丢失02、脏读&#xff08;Dirty R…

Python怎样读取URL生成PDF

1. 安装依赖的exe 需要在这个网址&#xff0c;安装一个exe包&#xff0c;地址&#xff1a;https://wkhtmltopdf.org/ 进入网址后&#xff0c;点这个位置&#xff1a; 选择一个你的操作系统的下载链接&#xff1a; 安装后的exe文件&#xff1a; C:\Program Files\wkhtmltopdf…

分布式服务框架zookeeper+消息队列kafka

一、zookeeper概述 zookeeper是一个分布式服务框架&#xff0c;它主要是用来解决分布式应用中经常遇到的一些数据管理问题&#xff0c;如&#xff1a;命名服务&#xff0c;状态同步&#xff0c;配置中心&#xff0c;集群管理等。 在分布式环境下&#xff0c;经常需要对应用/服…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十四章 注册字符设备号

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

硅谷裸机云大宽带服务器连接不上是怎么回事?该如何处理

硅谷裸机云大宽带服务器连接不上的常见原因主要有网络设置、网络设备、服务端、软件和服务、物理层等&#xff0c;出现以上问题&#xff0c;RAK部落小编建议大家可以通过以下一系列的方法进行排查和解决。具体分析如下&#xff1a; 1.检查网络设置   核对配置信息&#xff1a…

微信小程序-CANVAS写入图片素材、文字等数据生成图片

微信小程序中&#xff0c;CANVAS写入图片素材、文字等数据生成图片&#xff0c;最终可将生成的 base64 格式图片保存至相册操作 Tips&#xff1a; 1、canvas 标签默认宽度 300px、高度 150px canvas 生成图片时&#xff0c;写入图片素材、文字等数据前&#xff0c;需要根据实…

LangChain的使用详解

一、 概念介绍 1.1 Langchain 是什么&#xff1f; 官方定义是&#xff1a;LangChain是一个强大的框架&#xff0c;旨在帮助开发人员使用语言模型构建端到端的应用程序&#xff0c;它提供了一套工具、组件和接口&#xff0c;可简化创建由大型语言模型 (LLM) 和聊天模型提供…

nfs和web服务器的搭建

&#xff08;一&#xff09;web服务器的搭建 1.配置基本环境 要点有&#xff0c;yum源&#xff0c;包含nginx和阿里云&#xff08;或者腾讯云或者华为云&#xff09;&#xff0c;这里的相关知识可以参考之前的yum配置笔记 2.安装nginx yum -y install nginx 3.验证并且开启服…

VS Code 配置 C/C++ 编程运行环境(保姆级教程)

文章目录 一、软件下载1. 下载 VS Code 安装工具 2. 下载 MinGW-W64二、安装 VS Code三、安装 MinGW-W64 及配置环境变量四、配置 VS Code 的 C/C 编程运行环境1. 汉化 VS Code&#xff08;选做&#xff09;2. 安装 C/C 扩展包 五、测试 VS Code 的 C/C 编程环境1. 创建代码文件…

C++学习笔记01-语法基础(问题-解答自查版)

前言 以下问题以Q&A形式记录&#xff0c;基本上都是笔者在初学一轮后&#xff0c;掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系&#xff0c;也适合做查漏补缺和复盘。 本文对读者可以用作自查&#xff0c;答案在后面&#xff0…

求职学习day8

7/21回顾&#xff1a; 用面试鸭的意义可能就在于将知识点用问答的形式具象化在脑海&#xff0c;不然可能只停留在听说过的感觉 7.21 玩了一天。一个很不好的信号。今天下午要试试把 mall 项目的代码运行过一遍。 项目运行问题&#xff1a; 问题 1 &#xff1a;两个门服务器…

【HarmonyOS】HarmonyOS NEXT学习日记:六、渲染控制、样式结构重用

【HarmonyOS】HarmonyOS NEXT学习日记&#xff1a;六、渲染控制、样式&结构重用 渲染控制包含了条件渲染和循环渲染&#xff0c;所谓条件渲染&#xff0c;即更具状态不同&#xff0c;选择性的渲染不同的组件。 而循环渲染则是用于列表之内的、多个重复元素组成的结构中。 …

Redis集群部署Windows版本

Redis集群 之前因为数据量的原因&#xff0c;并没有进行Redis集群的配置需要&#xff0c;现在由于数据量大&#xff0c;需要进行集群部署。 最初在windows系统部署&#xff0c;需要Redis的windows版本&#xff0c;但官方没有windows版本&#xff0c;所以需要去gitHub上找由民…

R语言统计分给——数据管理2

参考资料&#xff1a;R语言实战【第2版】 1、日期值 日期值通常以字符串的形式输入R中&#xff0c;然后转化为数值形式存储的日期边阿玲。函数as.Date()用于执行这种转化。其语法为as.Date(x,"input_format")&#xff0c;其中x是字符型数据&#xff0c;input_format…

Ubuntu 24.04 LTS Noble安装 FileZilla Server

FileZilla Server 是一款使用图形用户界面快速创建 FTP 服务器的软件。它有助于测试需要 FTP 服务器功能的各种项目。虽然早期的 FileZilla FTP 服务器仅适用于 Windows 和 macOS&#xff0c;但现在我们也可以在 Linux&#xff08;例如 Ubuntu 24.04&#xff09;上安装 FileZil…

CatBoost模型Python代码——用CatBoost模型实现机器学习

一、CatBoost模型简介 1.1适用范围 CatBoost&#xff08;Categorical Boosting&#xff09;是一种基于梯度提升的机器学习算法&#xff0c;特别适用于处理具有类别特征的数据集。它可以用于分类、回归和排序任务&#xff0c;并且在处理具有大量类别特征的数据时表现优异。典型…

安装好anaconda,打开jupyter notebook,新建 报500错

解决办法&#xff1a; 打开anaconda prompt 输入 jupyter --version 重新进入jupyter notebook&#xff1a; 可以成功进入进行代码编辑

你了解你的GD32 MCU系统主频是多少吗 ?

系统时钟是GD32 MCU的时基&#xff0c;可以理解为系统的心跳&#xff0c;片上所有的外设以及CPU最原始的时钟都来自于系统时钟&#xff0c;因而明确当前系统时钟是多少非常重要&#xff0c;只有明确了系统时钟&#xff0c;才能够实现准确的定时、准确的采样间隔以及准确的通信速…

回溯题目的套路总结

前言 昨天写完了LeeCode的7&#xff0c;8道回溯算法的题目&#xff0c;写一下总结&#xff0c;这类题目的共同特点就是暴力搜索问题&#xff0c;排列组合或者递归&#xff0c;枚举出所有可能的答案&#xff0c;思路很简单&#xff0c;实现起来的套路也很通用&#xff0c;一…