Linux_线程控制

线程控制的相关接口

进程创建相关

之前我们已经认识到了pthread_create函数用来创建线程,这里不再赘述。

pthread_self函数

void* routine(void* args)
{std::cout  << "我是新线程..." << pthread_self() << std::endl;return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void*)"thread");while(true)    sleep(1);return 0;
}

通过结果我们可以发现,得到了一个数字。实际上,使用 pthread_self 得到的这个数实际上是虚拟地址空间上的一个地址,通过这个地址,可以找到关于这个线程的基本信息,包括线程ID,线程栈,寄存器等属性;而真正的线程ID是通过 ps -aL | head -1 && ps -aL | grep mythread 命令得到的。

进程等待相关

为什么需要线程等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。
  • 若不等待,就会有内存泄露的问题。

pthread_join函数

参数说明

  • pthread_t thread:这是要等待的线程的标识符(ID),该标识符是由 pthread_create 函数返回的。
  • void **retval:这是一个指向 void * 指针的指针,用于接收被等待线程的返回值。

retval参数的可能取值(下面代码中都会体现):

  1. 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。
  2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,retval所指向的单元里存放的是常数PTHREAD_ CANCELED(-1)。
  3. 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。

如果成功,pthread_join 返回 0;如果失败,则返回错误码。

 ps. 如果需要退出的线程一直不退出,那么等待该线程的线程就会一直等待!

void* routine(void* args)
{std::cout  << "我是新线程..." << pthread_self() << std::endl;return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void*)"thread");// 等待int n = pthread_join(tid1, nullptr);if(n != 0)std::cerr << "join error: "<< n << ", " << strerror(n) << std::endl;std::cout << "join success!" << std::endl;while(true)    sleep(1);return 0;
}


我们将代码稍作修改

void* routine(void* args)
{while(true){std::cout  << "我是新线程..." << toHex(pthread_self()) << std::endl;sleep(1);break;}// 返回值:可以是变量、数字、对象!return (void*)10;    // 将返回结果修改为10
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void*)"thread");void *ret = nullptr;int n = pthread_join(tid, &ret);// 等待成功并打印ret的值std::cout << "join success!, ret: " << (long long int)ret << std::endl;return 0;
}

结论:ret的值其实就是routine的返回值!

ps. 理论上,堆空间也是共享的!谁拿着堆空间的入口地址,谁就能访问该堆区!栈空间也是如此!

Demo代码

// 创建多线程并等待的样例(传递参数为对象)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <pthread.h>#define NUM 5class ThreadDate
{
public:ThreadDate(){}void Init(int a, int b, std::string name){   _a = a;_b = b;_name = name;}int Result()    {return _a + _b; }std::string Name(){return _name;}void SetId(pthread_t id){ _tid = id;}pthread_t Id(){return _tid;}int A(){return _a;}int B(){return _b;}~ThreadDate(){}
private:int _a;int _b;int _result;std::string _name;pthread_t _tid;
};void* routine(void* args)
{ThreadDate* td = static_cast<ThreadDate*>(args);std::cout << "我是新线程,我的名字是: " << td->Name() << std::endl;return nullptr;
}
int main()
{ThreadDate td[NUM];// 预处理for(int i = 0;i < NUM;i++){char id[64];snprintf(id, sizeof(id), "thread-%d", i);td[i].Init(i * 10, i * 20, id);}// 创建多个线程for(int i = 0;i < NUM;i++){pthread_t tid;pthread_create(&tid, nullptr, routine, &td[i]);td[i].SetId(tid);}// 等待多个线程for(int i = 0;i < NUM;i++){pthread_join(td[i].Id(), nullptr);}// 汇总处理结果for(int i =0;i < NUM; i++){printf("td[%d]: %d+%d=%d[%ld]\n", i, td[i].A(), td[i].B(), td[i].Result(), td[i].Id());}return 0;
}

进程终止模块

如果需要只终止某个线程而不终止整个进程,有三种方法:

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。

pthread_exit函数

 参数:

  • retval:这是一个指向任意数据的指针,该数据将被线程的终止状态所使用,并且可以被其他线程通过调用 pthread_join 来访问。
void* routine(void* args)
{std::string name = static_cast<const char*>(args);std::cout << "我是新线程..." << std::endl;pthread_exit((void*)10);    // 作用与return相同
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void*)"thread");void* ret = nullptr;pthread_join(tid, &ret);std::cout << "new thread exit code: " << (long long int)ret << std::endl;return 0;
}

pthread_cancel函数

参数:

  • thread:要发送取消请求的线程标识符(pthread_t 类型)。
void* routine(void* args)
{std::string name = static_cast<const char*>(args);while(true){std::cout << "我是新线程..." << std::endl;sleep(1);}//该线程没有退出
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void*)"thread");sleep(2);pthread_cancel(tid);std::cout << "取消线程: " << tid << std::endl;sleep(2);void* ret = nullptr;// 取消之后的线程的返回值为:-1:PTHREAD_CANCELED ((void *) -1)// 所以取消之后的线程也必须join,否则就会内存泄漏pthread_join(tid, &ret);    std::cout << "new thread exit code: " << (long long int)ret << std::endl;return 0;
}

线程分离模块

pthread_detach函数

void* routine(void* args)
{// 线程分离可以自己主动分离// pthread_detach(pthread_self());std::string name = static_cast<const char*>(args);while(true){std::cout << "我是新线程..." << std::endl;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void*)"thread");// 线程分离也可以被主线程分离pthread_detach(tid);sleep(2);    //先让线程分离,在进行等待void* ret = nullptr;int n = pthread_join(tid, &ret);// pthread_join返回0代表等待成功,非0为失败std::cout << "new thread exit code: " << (long long int)ret << ", n: " << n <<  std::endl;return 0;
}

线程分离之后,pthread_join就不会阻塞等待被分离的线程,所以join就会失败。

线程局部存储

线程局部存储(Thread Local Storage,TLS)是一种特殊的存储机制,用于为每个线程提供独立的变量副本,确保线程之间的数据隔离。

__thread关键字实现线程局部存储

__thread int tls_variable = 0; // 每个线程都有独立的tls_variable副本

ps. __thread只能修饰内置类型 

优点:

  • 线程安全:每个线程访问自己的变量副本,避免了线程之间的数据竞争。

  • 性能优化:减少了锁的使用,提高了多线程程序的性能。

  • 灵活性:可以存储线程相关的上下文信息。

缺点:

  • 内存占用:每个线程都会有一个独立的变量副本,可能会增加内存占用。

  • 线程生命周期管理:需要确保线程结束时清理线程局部存储的资源,否则可能导致内存泄漏。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[java] 面向对象进阶篇1--黑马程序员

目录 static 静态变量及其访问 实例变量及其访问 静态方法及其访问 实例方法及其访问 总结 继承 作用 定义格式 示例 总结 子类不能继承的内容 继承后的特点 成员变量 成员变量不重名 成员变量重名 super访问父类成员变量 成员方法 成员方法不重名 成员方法…

TCP 三次握手四次挥手

目录 TCP 三次握手 1. SYN (Synchronize&#xff1a;同步) 2. SYN-ACK (Synchronize Acknowledge&#xff1a;同步确认) 3. ACK (Acknowledge&#xff1a;确认) 为什么是三次而不是两次或四次&#xff1f; 三次握手的作用 TCP 四次挥手 第一次挥手&#xff1a;客户端发送 FIN …

Vue2下篇

插槽&#xff1a; 基本插槽&#xff1a; 普通插槽&#xff1a;父组件向子组件传递静态内容。基本插槽只能有一个slot标签&#xff0c;因为这个是默认的位置&#xff0c;所以只能有一个 <!-- ParentComponent.vue --> <template> <ChildComponent> <p>…

第38周:猫狗识别 (Tensorflow实战第八周)

目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 输出 二、数据预处理 2.1 加载数据 2.2 再次检查数据 2.3 配置数据集 2.4 可视化数据 三、构建VGG-16网络 3.1 VGG-16网络介绍 3.2 搭建VGG-16模型 四、编译 五、训练模型 六、模型评估 七、预测 总结 前言…

具身智能与大模型融合创新技术实训研讨会成功举办

2025年1月16日-19日武汉&#xff0c;TsingtaoAI联合北京博创鑫鑫教育科技&#xff0c;举行“具身智能与大模型融合创新技术”实训研讨会&#xff0c;本次会议面向高校AI教师和企业AI工程师群体&#xff0c;通过3天的技术研修和实操教学&#xff0c;通过将 AI 大模型与具备3D视觉…

OpenAI的工具革命: 当Operator撕开中国AI「内卷式创新」的遮羞布

OpenAI最新发布的智能体Operator&#xff0c;并非简单的任务执行工具&#xff0c;而是一场针对「工具的工具」的底层革命。它用通用性智能体架构重构人机协作范式&#xff0c;而中国AI产业仍在「卷场景」「卷补贴」的泥潭中打转。这场降维打击背后&#xff0c;暴露的是中美AI竞…

MySQL(1)

数据库 基础篇 MYSQL概述 SQL 函数 约束 多表查询 事务 进阶篇 存储索引 索引 SQL优化 试图/存储过程/触发器 锁 InnoDB核心 MySQL管理 运维篇 日志 主从复制 分库本表 读写分离 基础篇 MySQL 数据库概念&#xff1a;存储数据的仓库&#xff0c;数据是有…

SpringBoot+Vue使用Echarts

前言 在vue项目中使用echarts&#xff0c;本次演示是使用vue2 1 前端准备 echarts官网&#xff1a; https://echarts.apache.org/zh/index.html 官网提供了基本的使用说明和大量的图表 1.1 下载echarts 执行命令 npm install echarts 直接这样执行很可能会失败&#xff0c;…

PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(下.代码部分)

医疗 MLLM 框架编程实现 本医疗 MLLM 框架结合 Python 与 PyQt6 构建,旨在实现多模态医疗数据融合分析并提供可视化界面。下面从数据预处理、模型构建与训练、可视化界面开发、模型 - 界面通信与部署这几个关键部分详细介绍编程实现。 6.1 数据预处理 在医疗 MLLM 框架中,多…

Linux-day10

第21章 Linux高级篇-日志管理 日志介绍和实例 基本介绍 系统常用的日志 日志服务 日志服务原理图 在这个配置文件里面记录了日志服务程序 日志管理服务rsyslogd -v是反向匹配 invert 日志服务配置文件 时间、主机、是由哪个程序或者服务发生的、事件信息 自定义日志服务 日…

Linux第一讲--基本的命令操作

从今天开始&#xff0c;我将在csdn这个平台上和大家分享Linux的相关知识&#xff0c;欢迎大家一起讨论&#xff01; 零、基本操作 1.进入全屏&#xff1a; ALTENTER,退出也是这个 2.复制&#xff1a;ctrlinsert 3.粘贴&#xff1a;shiftinsert Linux中&#xff0c;cv是不好…

WinRAR.exe命令行的使用

工具 命令行打包命令 rem 默认压缩根目录&#xff0c;递归处理子文件夹使用 -r WinRAR.exe a -r test.rar C:/web/Views/

### 2.5.3 二叉树的基本操作

2.5.3 二叉树的基本操作 // 获取树中节点的个数 int size(Node root);// 获取叶子节点的个数 int getLeafNodeCount(Node root);// 子问题思路-求叶子结点个数// 获取第K层节点的个数 int getKLevelNodeCount(Node root,int k);// 获取二叉树的高度 int getHeight(Node root);…

设计新的 Kibana 仪表板布局以支持可折叠部分等

作者&#xff1a;来自 Elastic Teresa Alvarez Soler, Hannah Mudge 及 Nathaniel Reese 在 Kibana 中构建可折叠仪表板部分需要彻底改造嵌入式系统并创建自定义布局引擎。这些更新改进了状态管理、层次结构和性能&#xff0c;同时为新的高级仪表板功能奠定了基础。 我们正在开…

怎么样把pdf转成图片模式(不能复制文字)

贵但好用的wps&#xff0c; 转换——转为图片型pdf —————————————————————————————————————————— 转换前&#xff1a; 转换后&#xff1a; 肉眼可见&#xff0c;模糊了&#xff0c;且不能复制。 其他免费办法&#xff0c;参考&…

PAT甲级-1023 Have Fun with Numbers

题目 题目大意 一个数乘以2倍后&#xff0c;仍由原来的数字组成&#xff0c;只不过顺序发生变化&#xff0c;就输出Yes&#xff0c;否则输出No。并输出乘以2部后的数。 思路 题目说数字不超过20位&#xff0c;long long最多只能表示19位&#xff0c;93....&#xff0c;超过其…

系统架构设计师教材:信息系统及信息安全

信息系统 信息系统的5个基本功能&#xff1a;输入、存储、处理、输出和控制。信息系统的生命周期分为4个阶段&#xff0c;即产生阶段、开发阶段、运行阶段和消亡阶段。 信息系统建设原则 1. 高层管理人员介入原则&#xff1a;只有高层管理日恩怨才能知道企业究竟需要什么样的…

CNN-BiLSTM卷积双向长短期记忆神经网络时间序列预测(Matlab完整源码和数据)

CNN-BiLSTM卷积双向长短期记忆神经网络时间序列预测&#xff08;Matlab完整源码和数据&#xff09; 目录 CNN-BiLSTM卷积双向长短期记忆神经网络时间序列预测&#xff08;Matlab完整源码和数据&#xff09;预测效果基本介绍 CNN-BiLSTM卷积双向长短期记忆神经网络时间序列预测一…

我谈区域偏心率

偏心率的数学定义 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》P312 区域的拟合椭圆看这里。 Rafael Gonzalez的二阶中心矩的表达不说人话。 我认为半长轴和半短轴不等于特征值&#xff0c;而是特征值的根号。…

每日进步一点点(网安)

1.1 level5 查看源码关键部分 $str strtolower($_GET["keyword"]); $str2str_replace("<script","<scr_ipt",$str); $str3str_replace("on","o_n",$str2);<input namekeyword value".$str3.">关键…