当前位置: 首页 > news >正文

嵌入式Linux:注册线程清理处理函数

在 Linux 多线程编程中,线程终止时可以执行特定的清理操作,通过注册线程清理函数(thread cleanup handler)来实现。这类似于使用 atexit() 注册进程终止处理函数。线程清理函数用于在线程退出时执行一些资源释放或清理工作,例如关闭文件描述符、释放内存等。

不同于进程,线程可以注册多个清理函数,这些清理函数以栈的形式管理,栈是一种先进后出的数据结构。因此,清理函数的执行顺序与注册顺序相反。

在 Linux 中,使用 pthread_cleanup_push()pthread_cleanup_pop() 函数分别向线程的清理函数栈添加和移除清理函数。其原型如下:

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);

参数说明:

  • pthread_cleanup_push():用于将清理函数推入栈中。
    • routine: 指向清理函数的函数指针,清理函数没有返回值,并接受一个 void * 类型的参数。
    • arg: 传递给清理函数的参数,当清理函数执行时,该参数作为 routine() 的输入。
  • pthread_cleanup_pop():用于从清理函数栈中弹出最近添加的清理函数。
    • execute: 指定是否执行清理函数。如果为 0,则只移除清理函数而不执行它;如果为非 0,则不仅移除还会执行清理函数。

线程清理函数执行的场景:

  • 当线程调用 pthread_exit()退出时,清理函数会自动执行。
  • 当线程响应取消请求时(如通过 pthread_cancel()取消线程),清理函数会被执行。
  • 当通过非 0 参数调用 pthread_cleanup_pop() 时,栈顶的清理函数会被执行。

以下代码展示了如何使用 pthread_cleanup_push()pthread_cleanup_pop() 注册和移除清理函数:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>void cleanup(void *arg) {printf("Cleaning up: %s\n", (char *)arg);
}void *thread_function(void *arg) {pthread_cleanup_push(cleanup, "Resource 1");pthread_cleanup_push(cleanup, "Resource 2");// 模拟线程工作printf("Thread is running...\n");// 调用pthread_exit()会触发清理函数的执行pthread_exit(NULL);// 清理函数必须成对使用,因此即使退出后也要调用pthread_cleanup_poppthread_cleanup_pop(1);pthread_cleanup_pop(1);
}int main() {pthread_t thread;// 创建一个线程if (pthread_create(&thread, NULL, thread_function, NULL) != 0) {perror("Failed to create thread");return 1;}// 等待线程结束pthread_join(thread, NULL);return 0;
}

解释说明:

  • 线程中注册了两个清理函数,分别为 "Resource 1" 和 "Resource 2"。
  • 当线程调用 pthread_exit() 时,栈中的清理函数按后进先出的顺序执行,因此会先打印 "Cleaning up: Resource 2",再打印 "Cleaning up: Resource 1"。

注意事项:

  • pthread_cleanup_push()pthread_cleanup_pop() 并不是普通函数,而是宏实现的,必须在相同的作用域内成对出现,不能在代码中分开使用。
  • 清理函数只会在线程通过 pthread_exit() 或响应取消请求时执行。如果线程通过 return 语句退出,清理函数不会被执行。

通过使用 pthread_cleanup_push()pthread_cleanup_pop(),可以确保在线程终止时执行所需的清理操作,这在资源管理和异常处理中非常有用。清理函数的自动执行使得多线程编程中的资源释放更加简洁、安全。

http://www.lryc.cn/news/603048.html

相关文章:

  • Zynq SOC FPGA嵌入式裸机设计和开发教程自学笔记:硬件编程原理、基于SDK库函数编程、软件固化
  • 第五章:进入Redis的Hash核心
  • 设计模式实战:自定义SpringIOC(亲手实践)
  • 深度研究——OpenAI Researcher Agent(使用OpenAI Agents SDK)
  • EAP(基于事件的异步编程模式)
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘papermill’问题
  • 时间数字转换器TDC的FPGA方案及核心代码
  • 将 NI Ettus USRP X410 的文件系统恢复出厂设置
  • C#:基于 EF Core Expression 的高性能动态查询构建实战 —— 面向大数据量环境的查询优化方案(全是干货,建议收藏)
  • Day22-二叉树的迭代遍历
  • 代码随想录Day32:动态规划(斐波那契数、爬楼梯、使用最小花费爬楼梯)
  • 10:00开始面试,10:06就出来了,问的问题有点变态。。。
  • Jmeter 性能测试监控之ServerAgent
  • AT89C 系列单片机知识点总结
  • 基于VHDL的神经网络加速器设计实战
  • 基于亮数据 MCP 的 Trae 智能体,让规模化 Google 数据实时分析触手可及
  • DBAPI的SQL实现模糊查询的3种方案
  • git相关操作记录
  • C++初学者4——标准数据类型
  • Day 24:元组与os模块
  • STM32F4—电源管理器
  • 新华三H3CNE网络工程师认证—Telnet
  • 在 CentOS 中安装 MySQL 的过程与问题解决方案
  • 每日面试题16:什么是双亲委派模型
  • LINUX 728 SHELL:grep;sort;diff
  • mp核心功能
  • CDN架构全景图
  • 【JavaScript】箭头函数和普通函数的区别
  • 【AI论文】MegaScience:推动科学推理后训练数据集的前沿发展
  • Node.js + TypeScript 开发健壮的淘宝商品 API SDK