【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://xiahunao.cn/news/3267621.html

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

相关文章

使用API有效率地管理Dynadot域名,设置过期域名抢注请求

简介 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…

深入理解SQL中的INNER JOIN操作

本文介绍了INNER JOIN的定义、使用场景、计算方法及与其他JOIN的比较。INNER JOIN是关系数据库中常用的操作&#xff0c;用于返回两个表中匹配的行&#xff0c;只有在连接条件满足时才返回数据。本文详细解释了INNER JOIN的语法及其在一对多、多对多关系中的应用&#xff0c;通…

【github】使用KeepassXC 解决github Enable two-factor authentication (2FA) 第二因子认证

下载 https://github.com/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-Win64.msi 代理地址 https://dgithub.xyz/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-Win64.msi 由于该软件不允许截图&#xff0c;以下操作参考官网 …

如何检查代理IP地址是否被占用

使用代理IP时&#xff0c;有时候会发现IP仍然不可用&#xff0c;可能是因为已经被其他用户或者网络占用了。为了检测代理IP是否被占用&#xff0c;我们可以采用一些方法进行验证测试&#xff0c;以保证代理IP的有效性和稳定性。 1.ARP缓存方法 ARP缓存法是一种简单有效的检测代…

【Python面试题收录】Python编程基础练习题①(数据类型+函数+文件操作)

本文所有代码打包在Gitee仓库中https://gitee.com/wx114/Python-Interview-Questions 一、数据类型 第一题&#xff08;str&#xff09; 请编写一个Python程序&#xff0c;完成以下任务&#xff1a; 去除字符串开头和结尾的空格。使用逗号&#xff08;","&#…

kotlin示例

以下代码是我写的练习程序&#xff0c;更好的代码可以从这里查看&#xff1a;代码 生日卡片 package com.example.happybirthdayimport android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity…

使用echo写入多行文字到文件时换行的处理

目标 想使用echo写入如下内容到文件program.c里 #include<stdio.h> int main(){printf("hello!\n"); } 需要处理 1、如何处理行换 2、代码中的换行如何处理 实际例子 创建文件夹 mkdir test cd test chmod 777 . 创建文件写入内容 查看 cat -n program.c…

Flink入门(更新中)

目录 一、Flink 1.1 基本概念 1.1.1 flink简介 1.2 flink编程模版 1.3 常用概念 1.2.1 datastream 1.2.2 算子、Task 1.2.3 多流操作 1.2.6 时间语义 二、Flink编程实战(Java) 2.1 wordcount 一、Flink 1.1 基本概念 1.1.1 flink简介 1.图片介绍 性能&#xff1a…

[练习]如何使用递归算法?

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;算法(Java)&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1. 递归概述 2.汉诺塔问题 题目描述​编辑 题解 代码实现 3…

package.json中对peerDependencies的理解

peerDependencies只要是用来限制依赖的&#xff0c;最近在开发的时候有遇到这样的问题&#xff0c;所以研究了一下 "peerDependencies": {"vue/composition-api": "^1.0.5","vue/runtime-core": "^3.0.0","echarts&q…

【Android】Activity生命周期与五种启动模式

文章目录 生命周期返回栈Activity状态生命周期方法 启动模式standard模式singleTask模式singleTop模式singleInstance模式singleInstancePerTask模式配置方式 生命周期 返回栈 每个Activity的状态由它在Activity栈&#xff08;又叫“回退栈back stack”&#xff09;中的位置决…

【ACM出版】2024年教育人工智能国际学术会议(ISAIE 2024,9月6-8)

2024年教育人工智能国际学术会议&#xff08;ISAIE 2024&#xff09;将于2024年9月6-8日在中国西安举行。本届会议由西京学院主办。 会议主要围绕人工智能在教育领域的最新研究成果展开&#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师等提…

windows安装redis设置密码、修改端口、提供外部访问

windows安装redis设置密码、修改端口、提供外部访问 一、前言1. 设置密码2. 修改端口3. 允许外部访问4. 注意事项 一、前言 设置Redis在Windows上设置密码、修改端口以及允许外部访问&#xff0c;需要进行以下步骤&#xff1a; 下载地址 https://github.com/tporadowski/redi…

快速入门Jupyter notebook

快速入门 Jupyter notebook 一、前言&#xff08;一&#xff09;优点&#xff08;二&#xff09;特点&#xff08;三&#xff09;调用运行&#xff08;四&#xff09;新建 二、认识界面快捷键&#xff08;一&#xff09;三种模式&#xff08;1&#xff09;蓝色模式&#xff1a;…

中国森林地上和地下生物量碳变化数据集(2002-2021年)

中国森林地上和地下生物量碳变化数据集&#xff08;2002-2021年&#xff09; 数据介绍 为了量化中国近期全国性恢复工作的生态后果&#xff0c;过去20年森林生物量碳储量变化的空间显性信息至关重要。然而&#xff0c;在全国范围内进行长期生物量追踪仍然具有挑战性&#xff0c…

203、移除链表元素

1、题目描述 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输…

基于迁移学习的手势分类模型训练

1、基本原理介绍 这里介绍的单指模型迁移。一般我们训练模型时&#xff0c;往往会自定义一个模型类&#xff0c;这个类中定义了神经网络的结构&#xff0c;训练时将数据集输入&#xff0c;从0开始训练&#xff1b;而迁移学习中&#xff08;单指模型迁移策略&#xff09;&#x…

Layui修改表格分页为英文

Layui修改表格分页为英文 1.前言2.Laypage属性 1.前言 主要记录初次使用Layui没有好好看官方文档踩坑&#xff0c;修改了源码才发现可以自定义 使用的Layui版本2.9.14 2.Laypage属性 Laypage属性中带的有自定义文本的属性 示例代码 table.render({.......page: {skipText: …

状态机 XState 使用

状态机 一般指的是有限状态机&#xff08;Finite State Machine&#xff0c;FSM&#xff09;&#xff0c;又可以称为有限状态自动机&#xff08;Finite State Automation&#xff0c;FSA&#xff09;&#xff0c;简称状态机&#xff0c;它是一个数学模型&#xff0c;表示有限个…

硬核科普:什么是网络准入控制系统|网络准入控制系统四大品牌介绍

网络准入控制系统&#xff08;Network Access Control, NAC&#xff09;是一种用于确保只有授权设备和用户才能接入网络的安全技术。 本文将介绍几种常用的网络准入控制系统&#xff0c;帮助您更好地了解如何选择适合您企业的NAC系统。 网络准入控制的重要性和作用 网络准入控…