线程的pthread_create、pthread_join、pthread_exit、pthread_detach函数
线程的创建(pthread_create)
pthread_t tid;//本质是unsigned long类型,打印时得到的是该线程的虚拟地址int 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*)
- void *arg:传递给线程调用的函数的参数(可以是字符串、整型、类对象等)
- 返回值:线程的所有相关函数执行成功时均返回0,失败时均返回错误码
#include <iostream>
#include <string>
#include <cstring>
#include <pthread.h>
#include <unistd.h>class threadData {
public:std::string name;int num;
};// 线程函数:传递字符串
void* threadrun1(void* arg) {const char* name = static_cast<const char*>(arg);int cnt = 10;while (cnt--) {std::cout << "name = " << name << ", cnt = " << cnt << std::endl;sleep(1);}return nullptr;
}// 线程函数:传递整型
void* threadrun2(void* arg) {int num = *static_cast<int*>(arg);int cnt = 10;while (cnt--) {std::cout << "num = " << num << ", cnt = " << cnt << std::endl;sleep(1);}return nullptr;
}// 线程函数:传递类对象(堆分配)
void* threadrun3(void* arg) {threadData* data = static_cast<threadData*>(arg);int cnt = data->num;while (cnt--) {std::cout << "[ClassThread] Name: " << data->name << ", Count: " << cnt << std::endl;sleep(1);}delete data; // 释放堆内存return nullptr;
}int main() {pthread_t tid1, tid2, tid3;// 动态分配类对象(堆内存)threadData* myData = new threadData(); // 使用 new 分配myData->name = "ClassThread";myData->num = 8;// 创建线程int n1 = pthread_create(&tid1, nullptr, threadrun1, (void*)"thread 1");int num_arg = 42;int n2 = pthread_create(&tid2, nullptr, threadrun2, (void*)&num_arg);int n3 = pthread_create(&tid3, nullptr, threadrun3, (void*)myData); // 传递堆对象指针// 错误检查if (n1 || n2 || n3) {int error_code = n1 ? n1 : (n2 ? n2 : n3);std::cerr << "线程创建失败: " << strerror(error_code) << std::endl;delete myData; // 错误时释放堆内存return 1;}// 等待线程结束pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);// 注意:myData 已在 threadrun3 中被释放,此处无需重复 deletestd::cout << "\n所有线程执行完毕,主线程退出" << std::endl;return 0;
}

注意事项:多线程编程中,虽然每个线程有独立的栈空间,但主线程传递给子线程的类对象或结构体对象仍应通过 new 在堆上动态分配:
- 虽然栈空间独立,但整个进程的虚拟地址空间是共享的,因此线程间可通过指针访问任意内存地址(包括其他线程的栈)
- 主线程如果以
pthread_exit的方式退出,那么主线程栈上的对象会被销毁,此时子线程再访问已销毁的栈对象会导致悬空指针问题,而如果是堆对象的话只要子线程运行期间该对象未被释放,就可以避免悬垂指针(若主线程不以pthread_exit的方式退出时所有线程都退出就不会出现悬空指针问题)
线程的等待(pthread_join)
// 线程执行的函数:传递类对象(堆分配)
void* threadrun3(void* arg) {threadData* data = static_cast<threadData*>(arg);int cnt = data->num;while (cnt--) {std::cout << "[ClassThread] Name: " << data->name << ", Count: " << cnt << std::endl;sleep(1);}delete data; // 释放堆内存return (void*)111;//返回线程执行完该函数后的信息
} || //返回信息放入ret中Vvoid* ret = nullptr;//ret是指针变量,被分配了空间,使用pthread_join时都要有一个ret这种的一级指针存放返回信息|_____________________________|V
int pthread_join(pthread_t thread, void **retval);//pthread_join函数的原始状态||int pthread_join(tid,&ret);//实际中会写成
pthread_t thread:目标线程的标识符(pthread_t类型)void **retval:输出型参数,二级指针,用于存储目标线程的退出状态(可为NULL)- ret的类型不一定是void*可根据线程函数的返回值决定,但是pthread_join的第二个参数的类型必须是void**,如果不是需要强转
- 若多个线程尝试
pthread_join同一个线程,会引发未定义行为
线程函数的返回值
基本概念:线程的返回值可以是整型、字符串也可以是对象,且必须提供void*类型的返回值
#include <iostream>
#include <string>
#include <cstring>
#include <pthread.h>
#include <unistd.h>//线程执行函数
class threadData {
public:int Task(){int result = x + y;return result;}int x;int y;int result;
};//线程结果查询函数
class threadResult
{
public:std::string print(){return std::to_string(x) + "+" + std::to_string(y) + " = " + std::to_string(result);//这里的+表示追加字符串}
public:int result;int x;int y;
};// 线程函数:传递类对象(堆分配)
void* threadrun(void* arg) {threadData* data = static_cast<threadData*>(arg);threadResult* result = new threadResult();int cnt = 3;while(cnt){sleep(3);std::cout <<" run ... , cnt: " << cnt-- <<std::endl;result->result = data->Task();result->x = data->x;result->y = data->y;}delete data; // 释放堆内存return (void*)result;
}int main() {pthread_t tid;// 动态分配类对象(堆内存)threadData* myData = new threadData(); // 使用 new 分配myData->x = 1;myData->y = 2;// 创建线程int n = pthread_create(&tid, nullptr, threadrun, (void*)myData); // 传递堆对象指针// 错误检查if (n != 0) {std::cerr << "线程创建失败: " << strerror(n) << std::endl;delete myData; // 错误时释放堆内存return 1;}// 等待线程结束threadResult* result = nullptr;int m = pthread_join(tid, (void**)&result);if (m == 0 && result != nullptr) {std::cout << "子线程结果: " << result->print() << std::endl;delete result; // 主线程负责释放结果} else {std::cerr << "线程执行失败或未返回结果" << std::endl;}return 0;
}

多线程的创建
#include <iostream>
#include <vector>
#include <unistd.h>//打印线程地址
std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);//打印无符号长整型的return buffer;
}void* ThreadRun(void* args)
{std::string name = static_cast<const char*>(args);while(true){std::cout <<name << "is running " <<std::endl;sleep(1);break;}return args;//返回线程名
}const int num = 10;int main()
{std::vector<pthread_t> tids;//创建多线程for(int i = 0;i<num;i++){//1、线程的idpthread_t tid;//2、线程的名字char* name = new char[128];//每个名字的长度(缓冲区)snprintf(name,128,"thread-%d ",i+1);//格式化向name指向的缓冲区中打印pthread_create(&tid,nullptr,ThreadRun,name /*线程的名字*/);//3、保存所有线程的id消息tids.emplace_back(tid);//使用emplace_back而不是push_back向数组中插入,避免了临时对象的出现} //主线程等待所有线程跑完for(auto tid : tids){void* name = nullptr;//获取线程的返回信息pthread_join(tid,&name);//std::cout << PrintToHex(tid) <<" quit... " <<std::endl;std::cout << (const char*)name << " quit..." <<std::endl;delete (const char*)name;}return 0;
}
┌───────────────────────┐
│ 主线程流程 │
└──────────┬────────────┘│▼
┌───────────────────────┐
│ 创建线程ID容器 vector │
└──────────┬────────────┘│▼
┌───────────────────────┐
│ 循环创建10个子线程 │
│ ┌─────────┐ │
│ │i=0~9 │ │
│ ├────┬────┤ │
│ │分配name│ │
│ │内存(new char[128])│
│ ├────┼────┤ │
│ │格式化线程名 │
│ │snprintf(thread-X) │
│ ├────┼────┤ │
│ │创建线程 │
│ │pthread_create() │
│ ├────┼────┤ │
│ │保存tid到容器 │
│ │tids.emplace_back() │
│ └────┴────┘ │
└──────────┬────────────┘│▼
┌───────────────────────┐
│ 等待所有子线程完成 │
│ ┌─────────┐ │
│ │遍历tids容器│ │
│ ├────┬────┤ │
│ │等待线程 │ │
│ │pthread_join() │
│ ├────┼────┤ │
│ │获取返回指针 │
│ │void* name │
│ ├────┼────┤ │
│ │打印退出信息 │
│ │cout << name │
│ ├────┼────┤ │
│ │释放内存 │
│ │delete[] name │
│ └────┴────┘ │
└──────────┬────────────┘│▼
┌───────────────────────┐
│ 主线程退出 │
└───────────────────────┘

线程的终止与取消(pthread_exit、pthread_cancel)
//调用此函数后,当前线程终止,retval 会被传递给 pthread_join 的接收参数(类似于线程调用函数的返回值)
void pthread_exit(void *retval);int pthread_cancel(pthread_t thread);//不常用
- void *retval:线程退出时返回的指针
pthread_t thread:目标线程的标识符

注意事项:
1、主线程最好最后一个结束,防止主线程结束后导致的所有线程退出的问题
2、子线程的退出标志是其所调用的函数return了
3、exit()用于终止进程,如果在多线程时调用exit()会导致当前进程结束,所有主线程退出
4、pthread_exit()用于终止线程 == 子线程调用函数中的return
#include <iostream>
#include <pthread.h>
#include <unistd.h>void* thread_func(void* arg) {int* result = malloc(sizeof(int));*result = 42;pthread_exit(result); // 返回堆内存指针(充当函数的返回值)
}int main() {pthread_t tid;pthread_create(&tid, nullptr, thread_func, NULL);void* retval;pthread_join(tid, &retval);printf("Result: %d\n", *(int*)retval); // 输出 42free(retval); // 必须手动释放
}
5、主线程使用pthread_exit退出后,子线程还可以继续运行,但退出位置的后续代码不能执行
#include <iostream>
#include <pthread.h>
#include <unistd.h>void* thread_func(void* args)
{...} <——————————————————————————————————————————————————|
int main() { |pthread_t tid; |pthread_create(&tid, NULL, thread_func, NULL); |pthread_exit(NULL); // 主线程退出但子线程继续运行; ———— // 后续代码不会执行
}
6、 pthread_cancel退出线程时,该线程的返回信息是-1
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>void* thread_func(void* arg) {while (true) {printf("线程运行中...\n");sleep(1); // 取消点(sleep 是取消点函数)}return NULL;
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);sleep(2); // 等待线程运行// 发送取消请求pthread_cancel(tid);void* retval;pthread_join(tid, &retval);// 检查返回值if (retval == PTHREAD_CANCELED) {printf("线程被取消!返回值地址: %p\n", retval);} else {printf("线程正常退出,返回值: %p\n", retval);}return 0;
}
线程的分离(pthread_detach)
int pthread_detach(pthread_t thread);
- pthread_t thread:目标线程的标识符
适用场景:当线程无需返回数据,且主线程无需等待其结束时
功能:被分离线程终止后,系统会自动回收其栈和线程描述符,避免资源泄漏和僵尸线程的出现
注意事项:
1、若线程已经调用pthread_detach,则不能再调用pthread_join获取该线程的返回信息,否则线程退出并报错
2、若不分离且未调用 pthread_join,线程终止后仍占用资源,成为“僵尸线程”
3、主线程无需等待被分离的线程结束,且子线程调用的函数中传入pthread_self()可自行分离,且常常需要资源安全验证和同步机制
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>// 线程数据结构体(需动态分配)
typedef struct {int id;char* task_name;
} ThreadData;// 资源清理函数
void cleanup(void* arg) {ThreadData* data = (ThreadData*)arg;printf("[清理] 释放资源: Task%d (%s)\n", data->id, data->task_name);free(data->task_name);free(data);
}// 线程函数:子线程自行分离 + 资源安全验证
void* thread_func(void* arg) {// 注册清理函数(确保资源释放)pthread_cleanup_push(cleanup, arg);// 尝试自行分离(防御性编程)int detach_ret = pthread_detach(pthread_self());if (detach_ret != 0) {fprintf(stderr, "[子线程] 分离失败: %s\n", strerror(detach_ret));}ThreadData* data = (ThreadData*)arg;printf("[子线程] Task%d 执行: %s\n", data->id, data->task_name);sleep(2); // 模拟耗时操作// 正常退出时手动触发清理(参数0表示不执行清理)pthread_cleanup_pop(0);return NULL;
}int main() {pthread_t tid;// 动态分配线程数据(需确保资源安全)ThreadData* data = malloc(sizeof(ThreadData));data->id = 1;data->task_name = strdup("ProcessData");// 创建线程int create_ret = pthread_create(&tid, NULL, thread_func, data);if (create_ret != 0) {fprintf(stderr, "[主线程] 创建失败: %s\n", strerror(create_ret));free(data->task_name);free(data);return 1;}// 主线程尝试分离子线程(双重保障)int detach_ret = pthread_detach(tid);if (detach_ret != 0) {fprintf(stderr, "[主线程] 分离失败: %s\n", strerror(detach_ret));}printf("[主线程] 继续运行,不等待子线程\n");// 等待足够时间确保子线程完成(实际项目需条件变量同步)sleep(3); // 必须大于子线程的 sleep(2)return 0;
}


4、pthread_detach的返回值有多种不同的错误码
int ret = pthread_detach(tid);
if (ret == EINVAL) {fprintf(stderr, "线程已分离或不存在\n");
} else if (ret == ESRCH) {fprintf(stderr, "线程ID无效\n");
}
![]()
~over~
相关文章:
线程的pthread_create、pthread_join、pthread_exit、pthread_detach函数
线程的创建(pthread_create) pthread_t tid;//本质是unsigned long类型,打印时得到的是该线程的虚拟地址int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void*), void *arg ); pthread_t *thre…...
测试专项4:AI算法测试在测试行业中,该如何定位自己自述
这岗位到底干啥的? 打个比方: 你就像AI模型的“质检员产品经理风险顾问”三合一。 质检员: 别人造了个AI模型(比如人脸识别系统),你不能光看它实验室成绩好,得把它丢到现实里折腾:…...
QT-LINUX-Bluetooth蓝牙开发
BlueToothAPI QT-BlueToothApi Qt Bluetooth 6.8.2 官方提供的蓝牙API不支持linux。 D-Bus的API实现蓝牙 确保系统中安装了 BlueZ(版本需≥5.56),并且 Qt 已正确安装并配置了 D-Bus 支持。 默默看了下自己的版本.....D-BUS的API也不支持。 在 D-Bus 中,org 目录是 D-Bus…...
【经验总结】AUTOSAR架构下NvMBlock无效问题分析
目录 前言 正文 1.问题描述 2.问题原因 3.深入分析 3.1NvM_InvalidateNvBlock分析 3.2NvBlock无效后NvM_ReadBlock行为分析 3.3NvBlock无效后NvM_WriteBlock行为分析 4.总结 前言 最近在做所有NvMBlock测试的时候,发现一个NvMBlock始终无法测试成功(写入Block值 --&…...
STM32 的tf卡驱动
基于STM32的TF卡驱动的基本实现步骤和相关代码示例,主要使用SPI接口来与TF卡进行通信。 硬件连接 将TF卡的SPI接口与STM32的SPI引脚连接,通常需要连接SCK(时钟)、MOSI(主出从入)、MISO(主入从出)和CS(片选)引脚。 软件实现 初始化SPI 配置SPI的工作模式、时钟频率…...
stress-ng命令详解
stress-ng 是一款功能强大的 Linux 系统压力测试工具,能够模拟多种复杂负载场景,覆盖 CPU、内存、磁盘 I/O、进程调度等核心资源,帮助开发者验证系统在高负载下的稳定性与性能表现。以下是其核心功能、参数解析及实战案例。 一、工具简介与安…...
【C语言系列】数据在内存中存储
数据在内存中存储 一、整数在内存中的存储二、大小端字节序和字节序判断2.1什么是大小端?2.2练习2.2.1练习12.2.2练习22.2.3练习32.2.4练习42.2.5练习52.2.6练习6 三、浮点数在内存中的存储3.1练习3.2浮点数的存储3.2.1 浮点数存的过程3.2.2 浮点数取的过程 3.3题目…...
【中文翻译】第12章-The Algorithmic Foundations of Differential Privacy
由于GitHub项目仅翻译到前5章,我们从第6章开始通过大语言模型翻译,并导出markdown格式。 大模型难免存在错漏,请读者指正。 教材原文地址:https://www.cis.upenn.edu/~aaroth/Papers/privacybook.pdf 12 其他模型 到目前为止&…...
图解模糊推理过程(超详细步骤)
我们前面已经讨论了三角形、梯形、高斯型、S型、Z型、Π型6种隶属函数,下一步进入模糊推理阶段。 有关六种隶属函数的特点在“Pi型隶属函数(Π-shaped Membership Function)的详细介绍及python示例”都有详细讲解:https://lzm07.b…...
datawhale组队学习-大语言模型-task5:主流模型架构及新型架构
目录 5.3 主流架构 5.3.1 编码器-解码器架构 5.3.2 因果解码器架构 5.3.3 前缀解码器架构 5.4 长上下文模型 5.4.1 扩展位置编码 5.4.2 调整上下文窗口 5.4.3 长文本数据 5.5 新型模型架构 5.5.1 参数化状态空间模型 5.5.2 状态空间模型变种 5.3 主流架构 在预训…...
为什么后端路由需要携带 /api 作为前缀?前端如何设置基础路径 /api?
一、为什么后端路由需要携带 /api 作为前缀? 1. 区分 API 端点与其他路由 在 Web 应用程序中,后端不仅需要处理 API 请求,还可能需要处理静态资源(如 HTML、CSS、JS 文件)或其他服务(如 WebSocket&#x…...
C++ 关系运算符重载和算术运算符重载的例子,运算符重载必须以operator开头
在C中,运算符重载允许为用户定义的类型(类或结构体)赋予某些内置运算符的功能。下面是一个关于关系运算符重载()和算术运算符重载()的简单例子。 示例:复数类的运算符重载 将创建一…...
建造者模式 (Builder Pattern)
建造者模式 (Builder Pattern) 是一种创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 一、基础 1.1 意图 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 1.2 适用场景 当创建复杂对象的算法应该…...
MCU vs SoC
MCU(Microcontroller Unit,单片机)和SoC(System on Chip,片上系统)是两种不同的芯片类型,尽管它们都实现了高度集成,但在设计目标、功能复杂性和应用场景上存在显著差异。以下是两者…...
RAG 架构地基工程-Retrieval 模块的系统设计分享
目录 一、知识注入的关键前奏——RAG 系统中的检索综述 (一)模块定位:连接语言模型与知识世界的桥梁 (二)核心任务:四大关键问题的协调解法 (三)系统特征:性能、精度…...
(C语言)习题练习 sizeof 和 strlen
sizeof 上习题,不知道大家发现与上一张的习题在哪里不一样嘛? int main() {char arr[] "abcdef";printf("%zd\n", sizeof(arr));printf("%zd\n", sizeof(arr 0));printf("%zd\n", sizeof(*arr));printf(&…...
Unity Animation的其中一种运用方式
Animation是Unity的旧的动画系统,先说目的,其使用是为了在UI中播放动效,并且在动效播放结束后接自定义事件而设计的 设计的关键点在于,这个脚本不是通过Animation直接播放动画片段,而是通过修改AnimationState的nor…...
湖北楚大夫
品牌出海已成为众多企业拓展业务、提升竞争力的关键战略。楚大夫(chudafu.com)作为一家专注于品牌出海、海外网络营销推广以及外贸独立站搭建的公司,凭借其专业、高效、创新的服务模式,致力于成为中国企业走向国际市场的坚实后盾与得力伙伴。楚大夫通过综…...
框架的CVE漏洞利用 php类 java类 手工操作和自动化操作蓝队分析漏洞利用的流量特征
前言 php重要框架和基本的识别特征 php的主要是 tp框架 和 laravel 当然还有 yii等 tp的主要特征 1\报错信息: 2、图标 3、请求头 Laravel特征 1、报错信息 2、请求头 php框架CVE利用 lavarvel 工具 https://github.com/zhzyker/CVE-2021-3129 https://git…...
前端Wind CSS面试题及参考答案
目录 标准盒模型与 IE 盒模型的区别是什么?如何通过 box-sizing 属性切换这两种盒模型? 如何计算一个元素在标准盒模型下的总宽度(包含 margin、padding、border)? 父元素高度塌陷的原因是什么?请列举至少 3 种清除浮动的方法。 方法一:使用 clear 属性 方法二:使用…...
数据结构 -- 线索二叉树
线索二叉树 线索二叉树的概念 线索二叉树的作用 我们在进行中序遍历时,总是从根节点出发进行二叉树遍历,而当仅知道某一孩子节点的指针时,由于无法访问父节点,所以没有办法进行遍历。由此引入线索二叉树 【思考】①如何找到指定…...
【算法day19】括号生成——数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
括号生成 https://leetcode.cn/problems/generate-parentheses/description/ 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 左括号数必须大于右括号数,且小于等于n class Solution { publ…...
Qt5.15.2实现Qt for WebAssembly与示例
目录 1.什么是Qt for WebAssembly? 1.1 什么是 WebAssembly? 1.2 WebAssembly 的优势 1.3 什么是 Qt for WebAssembly? 1.4 Qt for WebAssembly 的特点 1.5 编译过程 1.6 运行时环境 注意!!!注意&am…...
好吧好吧,看一下达梦的模式与用户的关系
单凭个人感觉,模式在达梦中属于逻辑对象合集,回头再看资料 应该是一个用户可以对应多个模式 问题来了,模式的ID和用户的ID一样吗? 不一样 SELECT USER_ID,USERNAME FROM DBA_USERS WHERE USERNAMETEST1; SELECT ID AS SCHID, NA…...
HOW - DP 动态规划系列(三)(含01背包问题)
目录 一、01背包问题最直接的暴力解法动态规划解法 二、完全背包 通过几个算法的学习,理解和掌握动态规划来解决背包问题。 一、01背包问题 对于面试的话,掌握01背包和完全背包就够用了,最多可以再来一个多重背包。 如果这几种背包分不清&…...
Linux的文件上传下载的lrzsz库的安装与使用
以下是关于 Linux 下 lrzsz 库的安装与使用 的详细指南,适用于通过终端(如 SecureCRT、Xshell、MobaXterm 等)使用 ZMODEM 协议快速上传和下载文件: 一、lrzsz 简介 功能:提供 rz(接收文件)和 …...
在linux服务器部署Heygem
前言: Heygem官方文档上提供了基于windwos系统的安装方案。在实际使用过程中个人电脑的配置可能不够。这个时候如果服务器配置够的话,可以尝试在服务器上装一下。但是服务器一般都是linux系统的,于是这篇教程就出现了… 可行性分析 通读安装…...
图书管理系统系统-Java、SpringBoot、Vue和MySQL开发的图书馆管理系统
「springboot、vue图书馆管理系统.zip」 链接:https://pan.quark.cn/s/5a929a7e9450 分享一个图书管理系统,Java、SpringBoot、Vue和MySQL开发的图书馆管理系统 以下是对文本内容的总结: 项目概述 项目名称与背景: 项目概述 项…...
学生管理系统(需求文档)
需求: 采取控制台的方式去书写学生管理系统 分析: 初始菜单: “----------欢迎来到java学生管理系统----------” “1:添加学生” “2:删除学生” “3:修改学生” “4:查询学生” “5:…...
[c语言日寄]数据输入
【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是…...
