C/C++|关于“子线程在堆中创建了资源但在资源未释放的情况下异常退出或挂掉”如何避免?

文章目录

  • 主线程监控子线程状态并负责清理资源
  • 使用智能指针(RAII模式)
  • 线程清理处理函数(pthread_cleanup_push、pthread_cleanup_pop)
  • 使用资源管理器或资源吃集中管理资源
  • 通过信号或全局变量监控线程状态
  • 使主线程负责分配和释放资源

在 C/C++ 中处理子线程分配的动态资源因线程异常退出而无法释放的问题,可以采用以下方法。我们将逐条分析并给出示例代码。

主线程监控子线程状态并负责清理资源

使用智能指针管理堆中的资源。

#include <pthread.h>
#include <unistd.h>#include <iostream>void* threadFunc(void* arg) {int* data = new int(42);*(int**)arg = data;  // 将资源的地址传递给主线程sleep(1);// 模拟子线程崩溃pthread_exit(NULL);delete data;  // 正常情况下应该释放资源,但是这里不会执行return NULL;
}int main() {pthread_t thread;int* sharedData = NULL;pthread_create(&thread, NULL, threadFunc, &sharedData);// 等待子线程完成void* status;pthread_join(thread, &status);// 如果子线程未释放资源,主线程负责清理if (sharedData != NULL) {std::cout << "Cleaning up memory in main thread: " << *sharedData<< std::endl;delete sharedData;}return 0;
}

使用智能指针(RAII模式)

这里我们一般是在C++中,当然,在C语言里也可以做类似的封装。

智能指针(如 std::unique_ptr 或 std::shared_ptr)可以自动管理资源生命周期,即使子线程崩溃,也会在对象销毁时释放资源。这种方法利用了 RAII 模式,减少手动清理的复杂性。

#include <pthread.h>
#include <unistd.h>#include <iostream>
#include <memory>void* threadFunc(void* arg) {std::unique_ptr<int> data(new int(42));  // 使用智能指针分配资源std::cout << "Data in thread: " << *data << std::endl;sleep(1);// 模拟子线程崩溃pthread_exit(NULL);return nullptr;
}int main() {pthread_t thread;pthread_create(&thread, NULL, threadFunc, nullptr);pthread_join(thread, nullptr);// 完全不需要手动释放资源std::cout << "Resource cleanup is handled by unique_ptr." << std::endl;return 0;
}

线程清理处理函数(pthread_cleanup_push、pthread_cleanup_pop)

在子线程中注册清理函数,以确保无论线程如何退出(正常或异常),清理函数都会被调用并释放资源。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>void cleanup(void* arg) {free(arg);printf("Resource freed in cleanup handler\n");
}void* threadFunc(void* arg) {int* resource = (int*)arg;pthread_cleanup_push(cleanup, resource);  // 注册清理函数// 使用资源*resource = 10;printf("Resource used in thread: %d\n", *resource);// 模拟异常退出pthread_exit(NULL);pthread_cleanup_pop(0);  // 0 表示不自动调用清理函数return NULL;
}int main() {pthread_t thread;int* resource = (int*)malloc(sizeof(int));if (resource == NULL) {perror("Failed to allocate memory");return -1;}*resource = 5;// 创建子线程if (pthread_create(&thread, NULL, threadFunc, resource) != 0) {perror("Failed to create thread");free(resource);  // 如果线程创建失败,释放资源return -1;}// 等待子线程结束pthread_join(thread, NULL);return 0;
}

使用资源管理器或资源吃集中管理资源

在该方法中,创建一个资源管理器,集中管理所有线程分配的资源。资源管理器记录每个资源的生命周期,并在必要时自动回收。

#include <iostream>
#include <pthread.h>
#include <unordered_map>
#include <mutex>class ResourceManager {
public:void allocateResource(int threadID) {std::lock_guard<std::mutex> lock(mutex_);resource_[threadID] = new int(42); // 分配资源}
private:std::unordered_map<int, int*> resource_;std::mutex mutex_;
};ResourceManager manager;void* threadFunc(void* arg) {int threadId = *reinterpret_cast<int*>(arg);manager.allocateResource(threadId);    // 请求资源管理器分配资源pthread_exit(nullptr);                 // 模拟线程异常退出return nullptr;
}int main() {pthread_t thread1, thread2;int id1 = 1, id2 = 2;pthread_create(&thread1, nullptr, threadFunc, &id1);pthread_create(&thread2, nullptr, threadFunc, &id2);pthread_join(thread1, nullptr);pthread_join(thread2, nullptr);// 主线程可以在这里清理manager.releaseResource(id1);manager.releaseResource(id2);return 0;
}

通过信号或全局变量监控线程状态

使用信号或全局变量监控线程状态是实现线程间通信的一种方法。它允许主线程检测子线程的状态(例如,是否正在运行或已结束),并在必要时采取相应措施(如主动释放资源)。为了确保线程间的同步,通常需要借助 互斥锁(std::mutex) 或 原子变量(std::atomic) 来保证数据读写的安全性。
在下面这个示例中,我们定义了一个全局原子变量 thread_running 来表示子线程的状态。主线程会定期检查该变量的状态,并在子线程异常退出时主动释放资源。

在C语言中,我们没有 std::atomic 和 智能指针这样的高级特性,但可以通过 全局变量和互斥锁 来实现类似的功能。
在下面这个示例中,主线程通过一个全局变量 thread_running 来监控子线程的状态。子线程在开始运行时将 thread_running 设置为 1,表示正在运行;当异常退出或结束时,将 thread_running 设置为 0,表示已停止。主线程定期检查 thread_running 的状态,如果检测到子线程已停止,则进行清理操作。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int* shared_data = NULL;       // 堆区资源
int thread_running = 0;        // 线程状态标志
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  // 互斥锁保护资源void* threadFunc(void* arg) {pthread_mutex_lock(&mutex);shared_data = (int*)malloc(sizeof(int));   // 分配堆区资源if (shared_data == NULL) {perror("Failed to allocate memory");pthread_mutex_unlock(&mutex);pthread_exit(NULL);}*shared_data = 42;thread_running = 1;                         // 标记子线程正在运行pthread_mutex_unlock(&mutex);printf("Data in thread: %d\n", *shared_data);sleep(2);  // 模拟子线程运行时间// 模拟子线程异常退出,直接退出而不释放资源pthread_mutex_lock(&mutex);thread_running = 0;                         // 子线程即将退出pthread_mutex_unlock(&mutex);pthread_exit(NULL);
}int main() {pthread_t thread;// 创建子线程if (pthread_create(&thread, NULL, threadFunc, NULL) != 0) {perror("Failed to create thread");return 1;}// 主线程监控子线程状态while (1) {pthread_mutex_lock(&mutex);if (thread_running == 0 && shared_data != NULL) {// 子线程已退出,但资源未释放,主线程进行清理printf("Main thread cleaning up memory: %d\n", *shared_data);free(shared_data);shared_data = NULL;}pthread_mutex_unlock(&mutex);if (thread_running == 0) {break; // 子线程已退出,主线程退出监控循环}sleep(1); // 定期检查子线程状态}// 等待子线程退出pthread_join(thread, NULL);pthread_mutex_destroy(&mutex);return 0;
}

使主线程负责分配和释放资源

这里主要是针对C语言中,没有智能指针和RAII这样的机制,所以我们避免在子线程中直接分配资源,而是让主线程负责分配内存,然后将该资源指针传递给子线程使用。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>void* threadFunc(void* arg) {int* shared_resource = (int*)arg;*shared_resource = 10;pthread_exit(NULL);//模拟异常退出
}int main () {pthread_t thread;int* shared_resource = (int*)malloc(sizeof(int));if (shared_resource == NULL) {perror("Failed to allocate memory");return -1;}*shared_resource = 5;if (pthread_create(&thread, NULL, threadFunc, shared_resource) != 0) {perror("Failed to create thread");free(shared_resource);return -1;}pthread_join(thread, NULL);// 主线程负责释放资源free(shared_resource);return 0;
}

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

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

相关文章

Excel中截取中文地址转换为省、市、区

使用方法/步骤 首先我们在网页打上方方格子&#xff0c;进入官网&#xff0c;下载方方格子。 解压后进行安装&#xff0c;打开OFFIE中的EXCEL&#xff0c;可以发现新新添加一个DIY工具箱&#xff0c;其中的提取地址功能可以将字符串地址解析为省、市、区 如下图所示

OpenSIPS 转发 JSSIP 注册消息到 FreeSWITCH

OpenSIPS IP 地址&#xff1a;192.168.31.213 FreeSWITCH IP 地址&#xff1a; 192.168.31.166 转发注册消息的路由代码应该是&#xff1a; if (is_method("REGISTER")) {save("location", "r");if ($pr "ws" || $pr "wss&q…

人民法院报:环境侵权诉讼中“虚假鉴定意见”提供者的刑事责任

2023年8月&#xff0c;最高人民法院发布了《关于审理生态环境侵权责任纠纷案件适用法律若干问题的解释》和《关于生态环境侵权民事诉讼证据的若干规定》&#xff08;以下简称《规定》&#xff09;&#xff0c;旨在进一步健全完善生态环境审判法律适用规则体系&#xff0c;推动生…

React.lazy() 懒加载

概要 React.lazy() 是 React 16.6 引入的一个功能&#xff0c;用于实现代码分割&#xff08;code splitting&#xff09;。它允许你懒加载组件&#xff0c;即在需要时才加载组件&#xff0c;而不是在应用初始加载时就加载所有组件。这种方法可以显著提高应用的性能&#xff0c…

引入 axios,根据 api 文档生成调用接口

起步 | Axios Docs 安装 axios npm install axios 生成 api 调用接口【可选】 https://github.com/ferdikoomen/openapi-typescript-codegen 安装 npm install openapi-typescript-codegen --save-dev 然后执行生成代码 # http://localhost:8805/api/user/v3/api-docs&a…

Swift 扩展

Swift 扩展 Swift 是一种强大的编程语言&#xff0c;由苹果公司开发&#xff0c;用于iOS、macOS、watchOS和tvOS应用程序的开发。自2014年发布以来&#xff0c;Swift因其易于阅读和编写的语法、现代化的设计以及出色的性能而广受欢迎。本文将探讨Swift的一些关键特性&#xff…

2款使用.NET开发的数据库系统

今天大姚给大家分享2款使用.NET开发且开源的数据库系统。 Garnet Garnet是一款由微软研究院基于.NET开源的高性能、跨平台的分布式缓存存储数据库&#xff0c;该项目提供强大的性能&#xff08;吞吐量和延迟&#xff09;、可扩展性、存储、恢复、集群分片、密钥迁移和复制功能…

ARM-8 定位发布版本 pstree 程序的 main 地址

逆向时如何找到main&#xff0c;如下&#xff1a; 1.readelf -h pstree ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2s complement, little endian Versi…

13.UE5流星火雨,引导施法技能制作

2-15 流星火雨&#xff0c;引导施法技能制作、随机数_哔哩哔哩_bilibili 目录 1.为流星火雨添加按键映射 2.创建流星火雨的动画蒙太奇 3.实现播放动画蒙太奇的逻辑 ​编辑 4.定义发射一波流星火雨的发射物 5.使用动画通知释放流星火雨 1.为流星火雨添加按键映射 创建名为流…

Zabbix5 通过 Rsyslog 实现设备日志收集分析syslog及监控告警

一、概述 本文档详细描述了如何使用 Zabbix5 和 Rsyslog 实现对设备日志的收集、监控以及在满足特定条件下触发告警的完整流程&#xff0c;包括环境准备、Rsyslog 配置、Zabbix5 配置以及常见问题排查等内容。 二、环境准备 服务器环境 操作系统&#xff1a;CentOS&#xff08;…

开源竞争-利用kimi编程助手搭建小程序(11)

开源竞争&#xff1a; 当你无法彻底掌握技术的时候&#xff0c;你就开源这个技术&#xff0c;让更多的人了解这个技术&#xff0c;培养出更多的技术依赖&#xff0c;你会说&#xff0c;这不就是在砸罐子吗&#xff1f;一个行业里面总会有人砸罐子的&#xff0c;不是你先砸罐子&…

mybatis连接PGSQL中对于json和jsonb的处理

pgsql数据库表字段设置了jsonb格式&#xff1b;在java的实体里使用String或者对象转换会一直提示一个错误&#xff1a; Caused by: org.postgresql.util.PSQLException: ERROR: column "xx" is of type jsonb but expression is of type character varying 需要加一…

Web大型网站的性能测试要求和工具方法

Web大型网站的性能测试要求和工具方法涉及多个层面的考量&#xff0c;旨在确保网站在高并发访问、大数据量处理、复杂交互场景下仍能保持良好的用户体验和系统稳定性。以下是针对大型网站性能测试的主要要求和相应的工具与方法&#xff1a; 性能测试要求 1. 高并发处理能力&…

Java 处理 json 格式数据解析为 csv 格式

Java处理json格式数据解析为csv格式 如果不使用 JSON 工具库&#xff0c;你可以手动解析 JSON 格式字符串并将其转换为 CSV 格式字符串。 以下是一个简单示例&#xff0c;展示如何实现这一功能。 示例代码 下面的示例代码手动处理 JSON 字符串&#xff0c;将其转换为 CSV 格式字…

python+智谱AI-实现钉钉消息自动回复

python智谱AI-实现钉钉消息自动回复 实现了电脑窗口切换&#xff0c;截图识别未读消息&#xff0c;与语言模型交互后&#xff0c;将答案带入到钉钉窗口中。偷个懒&#xff0c;直接贴代码了&#xff0c;后续不断完善注释&#xff0c;如果遇到读不懂的地方&#xff0c;欢迎交流。…

贪心算法day3(最长递增序列问题)

目录 1.最长递增三元子序列 2.最长连续递增序列 1.最长递增三元子序列 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;我们只需要设置两个数进行比较就好。设a为nums[0]&#xff0c;b 为一个无穷大的数&#xff0c;只要有比a小的数字就赋值…

Javascript如何获取指定网页中的内容?

这两天有一个需求&#xff0c;就是通过JS去获取网页的内容&#xff0c;当然&#xff0c;除了今天我要分享的这个方法以外&#xff0c;其实通过Ajax的Get方法也是可以实现这个功能的&#xff0c;但是Ajax就比较麻烦一些了&#xff0c;如果只是单纯的想要获取一下纯内容&#xff…

在CentOS7传统部署wordpress

1 环境准备 所需环境说明CentOS7.9ip地址&#xff1a;10.0.0.7&#xff0c;可以上网PHP72系列软件下面会介绍MySQL数据库暴露端口3306&#xff0c;用户wordpress&#xff0c;库wordpressnginx版本任意wordpres v6.5.2代码下载地址&#xff1a;https://cn.wordpress.org/wordpr…

文献阅读 | Nature Methods:使用 STAMP 对空间转录组进行可解释的空间感知降维

文献介绍 文献题目&#xff1a; 使用 STAMP 对空间转录组进行可解释的空间感知降维 研究团队&#xff1a; 陈金妙&#xff08;新加坡科学技术研究局&#xff09; 发表时间&#xff1a; 2024-10-15 发表期刊&#xff1a; Nature Methods 影响因子&#xff1a; 36.1&#xff0…

vs2022搭建opencv开发环境

1 下载OpenCV库 https://opencv.org/ 下载对应版本然后进行安装 将bin目录添加到系统环境变量opencv\build\x64\vc16\bin 复制该路径 打开高级设置添加环境变量 vs2022新建一个空项目 修改属性添加头文件路径和库路径 修改链接器&#xff0c;将OpenCV中lib库里的o…