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

甘肃新叶装饰工程设计有限公司 2026/09/09 20:04:44


一、线程等待

#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++ 失去了其中一个平台的用户,总之支持跨平台性就是为了让更多用户使用。


未完待续!

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

广州市网站建设网站正在建设中

在现代软件开发中,数据库管理的自动化集成已成为提升团队效率的关键因素。Bytebase作为领先的数据库DevOps平台,通过其强大的API扩展功能,让数据库集

2026/06/30 12:16:30

荆门网站建设吉林网站建设

2025视觉AI效率革命:Swin Transformer如何重塑十大行业【免费下载链接】swin-tiny-patch4-window7-224项目地址: https://ai.git

2026/06/30 10:11:19

建设网站教程网站建设优化

Chrome标签页终极整理方案:Better OneTab完整指南【免费下载链接】better-onetab:bookmark_tabs: A better OneTab for Chr

2026/06/30 11:18:55

网站建设方案书合川网站建设

Skipper路由引擎处理CosyVoice3复杂URL匹配规则在现代AI语音合成系统的部署实践中,一个看似简单却极易被忽视的问题正频繁浮现:如何让像CosyVoice3这

2026/06/30 11:57:58

建设官方网站网站网站建设

3.1 环境搭建:5分钟为Claude Code接入国产大模型(附完整教程)引言Claude Code是一个强大的AI原生开发工具,但默认使用的是Claude模型。对于国内开发者来说,接入国产大模型不

2026/06/30 14:03:38

网站建设广州成都 网站建设

GPT-SoVITS语音合成延迟优化实战记录在当前AI驱动的语音交互浪潮中,个性化语音合成已不再是实验室里的“黑科技”,而是逐步走进直播间、智能客服、有声书创作甚至医疗辅助

2026/06/30 10:54:22

海淀网站建设太原网站建设

第一章:Open-AutoGLM手势控制适配在智能交互系统中,Open-AutoGLM 提供了一种基于大语言模型驱动的手势识别与控制机制。该框架通过融合视觉感知与自然语言理

2026/06/30 11:34:56

网站建设协议甘肃网站建设

PyTorch正则化技术:Dropout、Weight Decay实践在构建深度神经网络时,我们常常会遇到这样的尴尬局面:模型在训练集上表现近乎完美ÿ

2026/06/30 12:36:32

天津网站建设公司浙江省建设厅网站

救命神器2025专科生必看!10个AI论文平台深度测评与推荐2025年专科生论文写作必备工具测评随着AI技术的快速发展,越来越多的专科生开始借助AI论文平台提升写作效率。然

2026/06/30 12:07:29