【Linux】线程分离 | 线程库 | C++调用线程 | 线程局部存储

简介: 【Linux】线程分离 | 线程库 | C++调用线程 | 线程局部存储

1. 线程分离

1. 为什么要线程分离?


使用 pthread_join 默认是阻塞的 ,即主线程等待 新线程退出

在这个过程中,主线程会直接卡住,就没办法继续向后运行,也就什么都干不了


若主线程 想做其他事情 ,所以就提出了线程分离的概念


默认情况下,新创建的线程是joinable的

即 线程默认被创建出来时,必须被join的, 若不能被join,线程对应的资源就无法释放,进而造成内存泄漏问题


若不关心线程的返回值,join是一种负担,创建一个线程时,提前告诉它,要分离这个线程


2. 具体使用

输入 man pthread_detach

38ebf76e0e1e4db4a9032fea57f14381.png


参数为 要分离线程的线程id

一个线程被分离,就无法再被join,如果join,函数就会报错

792dd605efb54d879a01c93f105981eb.png

77b5aa2e701d4d0287b4641f84614ad1.png

刚开始有主线程和新线程,使用pthread_join 使主线程等待新线程退出

随着自定义函数循环结束,将返回值传给join,新线程结束,

在休眠5秒后,主线程结束

1d56f3cee5aa4f9ea5b6a113801ca022.png

a37af5df236a4971af38a8b0d1e1d594.png

由于使用线程分离后,就不能使用pthread_join ,所以运行可执行程序后会报错

3. 为什么有时候分离在调用join 会正常运行?

ce628cd210d64d848050397ce363641a.png

在自定义函数中自己把自己分离

3882bd31fc4b4f08987ca80ac4abcc8b.png



可执行程序运行后,发现并没有报错,分离和没分离是一样的


线程被创建的时候,谁先执行并不确定

当使用pthread_create 创建线程时,有可能新线程没有跑,而是主线程继续向下执行,进入join,

然后新线程才把自己分离

join时没有分离,join后才进行分离,所以会正常执行程序


2. 如何理解线程库?

5afe83636f764297a51b2bfbdd3e5ee2.png

761982aa2ef348028dc1aa926efad19f.png

自己形成的可执行程序,要跟库文件关联起来

库要加载到内存中,经过页表映射到地址空间的共享区中


进程中的多线程,可以随时访问库中的代码和数据

每个线程也都可以访问映射过来的pthread库


线程库也需要管理线程,先描述再组织

线程库创建类似的管理线程的TCB


a6113f1b01274046b12dfb5d73329bb8.png


创建进程时,在内核中存在LWP(轻量级进程),为了更好管理LWP,没办法给用提供线程接口,就必须使用pthread库来适配,对线程做管理,与LWP产生关联,包含库中的线程属性 即TCB


在库中通过自己定义的线程控制结构,把内核中的LWP控制起来


如何理解 先描述 在组织?


e8481d6088bb4b0e88b38f4494df7852.png

描述:

struct pthread 描述的是线程的其他的一些属性

线程局部存储 (后面会详细讲)

线程独立的栈

整体红色的框 作为一个结构体 等同于 线程的TCB 结构 进行描述

创建一个线程就有一个红色框


组织:

整体红色的框 作为一个结构体

把 每个结构体想象成数组, 可以聚合在一起

找线程,找红色框的起始地址即可 称为 线程ID

pthread_t 就是一个地址数据,用来标识线程相关属性集合、

这个地址是虚拟地址


3. C++中使用多线程

添加头文件 #include < thread>

f374d1b063ae44fd9265bf1eeb2246c1.png



使用 thread 创建对象th

想要执行什么方法,可以把方法传入对象中

通过对象 . 的方式 可以调用 join detach 等


c++底层是对原生线程库的封装

所以需要在makefile中添加pthread库


107fc40bf50648e08342cb0ed3075f51.png

可执行程序即可正常运行

4. 线程局部存储

e397e580da704f4d9f19e0a072acaad8.png

局部变量

局部变量在每个线程中是私有的


ab188e448fce4ad8bcfdb36cd0751ecd.png

cnt在自定义函数中作为局部变量,属于栈上的

每个线程都有自己的栈,所以cnt属于每个线程都有的

edc5499f8ee540569bc337b66a704b78.png

2d2a78a839c74e198b85c2f750d4cfb4.png

三个线程对应的cnt地址是不相同的

三个线程的栈是不同的,局部变量cnt开辟到不同的栈中

cnt是同一个变量,地址绝对不一样


在自定义函数内定义的 局部变量cnt 是在运行时开辟的

编译时就把代码编译好了

局部变量会转化为汇编,以栈顶或者栈底为参考点 减去或者加上 对应数字 就代表是开辟空间


85d11e4f5cb440d2b43dd34535402291.png


更改 ebp 和 esp 就可以切换栈

ebp 可以是 线程1 、线程2、线程3的栈底,根据调度的不同 ,在不同的栈中开辟不同的变量

全局变量

默认情况下,全局变量是所有线程共享的

4f94d7dc8c3c498f96ace087ae362641.png

创建全局变量g_val,并对其进行修改

15262a3b18734813a1ab0d8ca6c5b28d.png

当有多个线程对全局变量修改时,地址是相同的 ,说明全局变量是所有线程共享的

ba260ff37d964dd2bc143cc3bafa330f.png

全局变量在已初始化数据段处开辟的空间

若不想g_val 被全局共享 ,则加入 __thread 编译选项

可以构建每个线程之间的局部存储

0b933c1258e447bd9bf1d2ec4133d58e.png

3657a44a674c4308ab1be919cc0ac4d4.png

每个线程对应的地址是不一样的

说明全局变量g_val 在每个线程中各自有一份

5a2346cd6c354b02b32b86b6d498bbc8.png


修改后的全局变量 在 线程局部存储 当中

将原来的全局变量给 主线程 以及新线程对应的 线程局部存储 都拷贝一份

每个线程都私有一份,所以地址都不一样

相关文章
|
13天前
|
存储 编译器 Linux
动态链接的魔法:Linux下动态链接库机制探讨
本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入、延迟绑定以及地址无关代码等内容。
182 19
|
1月前
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
61 7
|
1月前
|
消息中间件 存储 安全
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
60 1
|
2月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
116 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
571 3
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
83 6
|
2月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
87 5
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
45 0