商业网站建设惠州网站建设

四川鑫兆亿物资回收有限公司 2026/09/09 19:40:08


一、线程等待

#include <iostream> #include <pthread.h> #include <cstdio> #include <stdlib.h> #include <ctime> #include <unistd.h> #include "Task.hpp" #include <vector> void* Routine(void* args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << "new thread" << name << std::endl; sleep(5); break; } return (void*)10; } //线程等待 int main() { pthread_t tid; pthread_create(&tid,nullptr,Routine,(void*)"thread_1"); //等待线程退出,否则会出现类似于进程僵尸问题 void* retval = nullptr; int n = pthread_join(tid,&retval); if(n == 0)//等待成功 { std::cout << "join success: " << (long long)retval << std::endl; } return 0; }

注意:使用 ps -aL 是查不到线程退出时的僵尸状态的,因为 ps -aL 查的是系统级的轻量级进程,在内核中已经把这个线程释放了,但是线程管理的相关消息在 pthread 库内部中还存在。

问题:pthread_join 获取到的退出消息,为什么没有退出信号?为什么没有进行异常分析

答:新线程出异常,进程全部退出,线程没机会 join 成功,所以线程不需要关系异常。

注意:Routine 的返回值不一定是整数,他可以是一个字符串、类对象等。

#include <iostream> #include <pthread.h> #include <cstdio> #include <stdlib.h> #include <ctime> #include <unistd.h> #include "Task.hpp" #include <vector> void* Routine(void* args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << "new thread" << name << std::endl; sleep(1); } } //线程等待 int main() { pthread_t tid; pthread_create(&tid,nullptr,Routine,(void*)"thread_1"); //等待线程退出,否则会出现类似于进程僵尸问题 sleep(5); pthread_cancel(tid);//主动杀掉指定线程 void* retval = nullptr; int n = pthread_join(tid,&retval); if(n == 0)//等待成功 { std::cout << "join success: " << (long long)retval << std::endl; } return 0; }

如果是主线程主动杀掉线程时,线程的退出消息为 -1(PTHREAD_CANCELED);

二、分离线程

默认情况下,新创建的线程是 joinable 的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。

如果不关心线程的返回值,join 是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。此时把线程设置成分离状态就行。

#include <iostream> #include <pthread.h> #include <cstdio> #include <stdlib.h> #include <ctime> #include <unistd.h> #include "Task.hpp" #include <vector> void* Routine(void* args) { std::string name = static_cast<const char*>(args); int cnt = 5; while(cnt--) { std::cout << "new thread" << name << std::endl; sleep(1); } return (void*)10; } //线程等待 int main() { pthread_t tid; pthread_create(&tid,nullptr,Routine,(void*)"thread_1"); sleep(2); pthread_detach(tid);//把线程设置分离状态 void* retval = nullptr; int n = pthread_join(tid,&retval); if(n == 0)//等待成功 { std::cout << "join success: " << (long long)retval << std::endl; } else { sstd::cout << "join erroe: " << n << std::endl; } return 0; }

如果把线程设置成分离状态,主线程还要等待线程的话,必然会等待失败。

注意:一旦线程被设置成分离状态,则主线程不能提前退,甚至主线程是个死循环。既然把线程分离了,但是一旦线程出了问题,例如:除0,野指针等,还是会导致整个进程崩掉。

三、线程 ID 和进程地址空间布局

首先 pthread 库也是库,要加载到内存,进而映射到当前进程的虚拟地址空间,以便支持线程控制,在之前的我们可以知道线程的 id 值是一个非常大的数字,现在我可以明确的是线程的 id 值就是一个地址!线程既然可以有多个,那么有的线程正在运行、正在退出、正在分离等,这么多的线程肯定是要被管理起来的,怎么管理?先描述,再组织;注意:这里的线程概念在 Linux 内核中是没有体现出来的,内核里只有轻量级进程的概念,所以线程概念只有在 pthread 库里面体现!所以描述线程,使用 stuct Tcb 来描述,不是在 Linux 内核中描述,而是在 pthread 库里面描述,而组织请看下图:

我们每创建一个线程,在虚拟地址空间里面的 pthread 库就会创建该线程的属性信息,这些属性信息包括:struct pthread 结构体(这个结构体就是上面说的 Tcb)、线程局部存储、线程栈。而线程 id 值或者说 tid 就是这个属性信息的起始地址!!因为每个线程的属性信息在虚拟地址空间上是连续的,所以我们可以联想成是一个数组来管理这些线程的属性信息。在 struct pthread 结构体里面有个变量 void * result 这个变量来存储线程退出的信息,也就是说当 Routine 函数的返回值起始就是赋值给这个变量 result ,而 pthread_join 获取到线程退出的信息也就是把这个变量 result 赋值给我们的传过去的变量。

我们创建线程的底层是调用 clone 系统调用,而 clone 函数是用来创建轻量级进程或者进程的,在 clone 中如果不用创建虚拟地址空间、页表等数据结构,那么就是要创建轻量级进程,即:只创建 PCB ,那么我们可以得出一个结论:每创建一个线程都会在系统层面上有对应的 PCB ,也就是说:struct pthread : struct task_struct = 1 :1,这个称为:1:1 式的用户及线程;而 PCB 承担线程的调度、保存轻量级进程的上下文数据等作用,而 struct pthread 结构体是在库里面描述线程的属性,只是 PCB 承担了线程的大部分属性,所以 struct pthread 里面的属性才会这么少。所以线程退出时,PCB 是直接释放的,所以我们使用 pa -aL 来查线程是查不到的,但是 struct pthread 还存在,所以我们要等待线程释放掉这个 struct pthread 结构体以及该线程的线程局部存储、线程栈。struct pthread 和 线程局部存储、线程栈我们叫做线程的描述信息。

线程局部性存储:

#include <iostream> #include <pthread.h> #include <cstdio> #include <stdlib.h> #include <ctime> #include <unistd.h> #include "Task.hpp" #include <vector> //此时还没有就行线程局部存储 pid_t id = 0; void* Routine(void* args) { std::string name = static_cast<const char*>(args); while (true) { std::cout << "new thread id: " << id << std::endl; id++; sleep(1); } return nullptr; } int main() { pthread_t tid; pthread_create(&tid,nullptr,Routine,(void*)"thread_1"); while (true) { std::cout << "main thread id : " << id << std::endl; sleep(1); } pthread_join(tid,nullptr); return 0; }

上面的出现这张情况的原因:因为线程局部性存储是给每个线程开辟一段栈空间,也就是上面这个 id 值的地址在每个线程的中的虚拟地址是不一样的:

注意:mmap 区域其实就是虚拟地址空间的共享区;上面图片中的线程栈是专门给线程使用的,而上上张图里面的主线程栈是给主线程使用的。

线程局部性存储:只能用来局部性存储内置类型,常见的是整型;

线程局部性存储的意义:可以让不同的线程用同样的变量名,访问不同的内存块,各自访问各自的局部存储。

注意:线程局部性存储这个工作/过程是编译器做的。

四、C++ 的多线程(代码示范)

#include <iostream> #include <unistd.h> #include <thread> void Routine(int cnt) { while (cnt--) { std::cout << "new thread : " << cnt << std::endl; sleep(1); cnt--; } } int main() { std::thread t(Routine,10);//创建线程 while(true) { std::cout << "main thread" << std::endl; sleep(1); } t.join();//线程等待 return 0; }
TestThread:TestThread.cc g++ -o $@ $^ -std=c++11 .PHONY:clean clean: rm -f TestThread

使用C++来编写线程,在 Linux 中能跑,而且把这份代码复制到 Windows 平台下也能跑,这证明 C++ 具有很强的跨平台性(把所有的平台对应的线程代码用 C++ 封装,对外提供统一的接口),C++ 多线程的本质是在Linux系统中 C++ 多线程操作对 pthread 库的封装。

问题:为什么 C++ 性特性支持要以年为单位?

答:从技术角度,他要把所有的平台对应的功能全部封装一遍。

问题:为什么所有语言都追求跨平台性?

答:Windows 和 Linux 背后的用户的百万级别的,如果 C++ 只支持其中的一方,就会导致 C++ 失去了其中一个平台的用户,总之支持跨平台性就是为了让更多用户使用。


未完待续!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

银川网站建设商洛网站建设

EmotiVoice:让数字人“声”动起来在一场虚拟偶像的直播中,观众听到她用温柔又略带伤感的声音讲述一段往事——语调起伏自然,情感真挚得几乎让人忘记这并非真

2026/06/30 12:14:30

南京网站建设网站建设广告

Bruce固件ESP32故障诊断:5大模块排查与最佳实践指南【免费下载链接】BruceFirmware for m5stack Cardputer, StickC and ESP32项目

2026/06/30 10:16:49

青岛网站建设湖南营销型网站建设

快速体验打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容:创建一个GRUB修复效率对比工具,展示传统手动修复流程与AI辅

2026/06/30 14:17:09

四平网站建设睢宁网站建设

文章目录基础Prompt EngineerLLMs 调用流式输出Tool Use上下文管理(记忆)、持久化LLM基础进阶MCPRAG向量数据库Query优化检索优化生成优化Agent架构ReAct模式

2026/06/30 13:04:04

电子商务网站建设网站建设书

矩阵运算的基础与优化1. 向量运算向量运算在数学和计算机科学中是基础且重要的操作,下面将详细介绍向量加法、多个向量求和以及矩阵与向量乘法等相关内容。1.1 向量加法对于向量 $x$ 和 $y$,它们的

2026/06/30 11:16:54

天津网站建设江津网站建设

Material Web Components终极指南:快速构建跨框架Material Design应用【免费下载链接】material-webMaterial Design Web C

2026/06/30 13:38:06

牡丹江网站建设公司的网站建设

快速体验打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容:创建一个性能对比Demo,展示传统分页和虚拟滚动两种方式处理大

2026/06/30 12:34:32

建设网站教程石家庄网站建设

UnrealPakViewer深度解析:解锁虚幻引擎Pak文件的完整可视化方案【免费下载链接】UnrealPakViewer查看 UE4 Pak 文件的图形化工具,支持 U

2026/06/30 11:27:25

如何建设网站孝感网站建设

HTML页面展示Miniconda安装进度条模拟效果在云开发环境、在线AI实验室或远程Jupyter平台中,用户点击“启动环境”后最怕什么?不是等待本身,而是

2026/06/30 11:54:58

住房和城乡建设部网站济宁网站建设

目录已开发项目效果实现截图开发技术介绍核心代码参考示例1.建立用户稀疏矩阵,用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园

2026/06/30 10:58:23