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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32外设之SPI的介绍

### STM32外设之SPI的介绍 SPI&#xff08;Serial Peripheral Interface&#xff09;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;主要用于EEPROM、FLASH、实时时钟、AD转换器等外设的通信。SPI通信只需要四根线&#xff0c;节约了芯片的管脚&#x…

浅谈语言模型推理框架 vLLM 0.6.0性能优化

在此前的大模型技术实践中&#xff0c;我们介绍了加速并行框架Accelerate、DeepSpeed及Megatron-LM。得益于这些框架的助力&#xff0c;大模型的分布式训练得以化繁为简。 然而&#xff0c;企业又该如何将训练完成的模型实际应用部署&#xff0c;持续优化服务吞吐性能&#xf…

初始 html

html 文件结构 html 标签是整个 html 文件的根标签(最顶层标签) head 标签中写页面的属性. body 标签中写的是页面上显示的内容 title 标签中写的是页面的标题 <html><head><title>这是一个标题</title></head><body></body> <…

springboot校园支付系统-计算机毕业设计源码36348

目 录 摘要 Abstract 1 绪论 1.1 研究背景与意义 1.2 开发技术和开发特点 1.3论文结构与章节安排 2 校园支付系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.…

The First项目报告:抗 MEV 交易的CoW Protocol什么?

2023年&#xff0c;当UNIswap推出UniswapX 时&#xff0c;市场迎接它的不是赞叹&#xff0c;而是一片争议。UniswapX被指抄袭 CoWSwap 和 1inch。Curve 官方称 1inch 和 CoWSwap 早已改变游戏规则&#xff0c;UniswapX 非首创。CoWSwap 强调其 Intent Based Trading 的先驱地位…

【Linux系列】 环境配置文件合并的艺术:从`.env`到`.env.combined`

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Fastadmin框架短视频系统视频知识付费源码

简介&#xff1a; FastAdmin框架短视频系统/视频知识付费源码/附带小说系统 系统视频支持包月、单独购买、观影卷等功能 源码附带小说系统 源码需要配置高服务器和VDN加速 图片&#xff1a; 下载地址&#xff1a;云盘下载 原文地址&#xff1a;Fastadmin框架短视频系统视…

计算机体系结构之多级缓存、缓存miss及缓存hit(二)

前面章节《计算机体系结构之缓存机制原理及其应用&#xff08;一&#xff09;》讲了关于缓存机制的原理及其应用&#xff0c;其中提出了多级缓存、缓存miss以及缓存hit的疑问。故&#xff0c;本章将进行展开讲解&#xff0c; 多级缓存、缓存miss以及缓存hit存在的意义是为了保持…

后端SpringBoot学习项目-用户管理-增删改查

最终代码结构 仓库地址 Entity文件 数据库表设计 entity层实现 文件创建 ● 创建entity文件夹 ● 在entity层创建Java类&#xff0c;名字为User (关键字不可使用) 代码实现 package com.example.drhtspringboot.entity;import com.baomidou.mybatisplus.annotation.IdT…

华为入围Linux 内核CVE 检视“五人团”,openEuler要再进阶?

背景&#xff1a;内核社区接管 Linux 社区漏洞发布 往年 Linux 内核漏洞发布存在来源不固定、覆盖不全面&#xff0c;有时发布无修复补丁的 CVE 从而形成 0-day 漏洞等问题&#xff0c;给 Linux 内核安全带来了不确定性&#xff0c;为了更规范化运作&#xff0c;2024 年 2 月 1…

【C语言指南】C语言内存管理 深度解析

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C语言指南》 期待您的关注 引言 C语言是一种强大而灵活的编程语言&#xff0c;为程序员提供了对内存的直接控制能力。这种对内存…

12 Node.js API接口开发

八、API接口 8.1 json-server工具 1&#xff09;安装json-server npm i -g json-server2)示例 //students.json {"student":[{"id":1,"name":"sally","age":18,"gender":"女"},{"id":2,&…

前段时间我所在的公司收到了来自Nevicat的律师函

前段时间我所在的公司收到了来自Nevicat的律师函&#xff0c;至于原因嘛&#xff0c;大家懂的都懂。肯定是因为没有购买人家的正版软件&#xff0c;于是公司下令&#xff0c;所有人禁止继续使用Nevicat自行寻找其他sql工具&#xff0c;迫于无奈&#xff0c;在我使用了十几款主流…

【系统设计】理解带宽延迟积(BDP)、吞吐量、延时(RTT)与TCP发送窗口的关系:优化网络性能的关键

在设计和优化网络性能时&#xff0c;理解 带宽延迟积&#xff08;BDP&#xff09;、吞吐量、延时&#xff08;RTT&#xff09; 和 TCP发送窗口 之间的关系至关重要。这些概念相互影响&#xff0c;决定了网络连接的性能上限&#xff0c;尤其是在高带宽、高延迟的环境中&#xff…

微服务容器化部署实践(FontConfiguration.getVersion)

文章目录 前言一、整体步骤简介二、开始实战1.准备好微服务2.将各个微服务打包为镜像第一种第二种3. 将各个打包好的镜像,通过docker-compose容器编排,运行即可总结前言 docker容器化部署微服务: 将微服务容器化部署到 Docker 容器中是一个常见的做法,可以提高应用的可移…

如何监控员工上网行为?五大妙招轻松上手,员工上网监控全攻略!挖到宝啦!

如何监控员工上网行为&#xff1f; 员工的不当上网行为不仅有可能导致企业机密的泄露&#xff0c;还可能对工作效率造成显著影响。 因此&#xff0c;如何有效地监控员工的上网行为&#xff0c;已成为许多企业管理者关注的重点。 本文&#xff0c;将为您介绍五大妙招&#xff…

【C++ 算法进阶】算法提升十一 十二

目录标题 让字符串成为回文串的最少插入次数题目题目分析代码题目题目 字符子串 &#xff08;滑动窗口&#xff09;题目题目分析代码 最长连续子序列 &#xff08;头尾表&#xff09;题目题目分析代码 让字符串成为回文串的最少插入次数 题目 本题为为LC原题 题目如下 题目分…

Linux(CentOS)安装 MySQL

CentOS版本&#xff1a;CentOS 7 三种安装方式&#xff1a; 一、通过 yum 安装&#xff0c;最简单&#xff0c;一键安装&#xff0c;全程无忧。 二、通过 rpm 包安装&#xff0c;需具备基础概念及常规操作。 三、通过 gz 包安装&#xff0c;需具备配置相关操作。 --------…

6.1 软件测试:软件质量与测试

软件质量与测试 1、软价质量保证1.1 软件质量质量控制QC&#xff1a;QUALITY CONTROL质量保证QA:QUALITY ASSURANCE质量成本软件质量软件质量保证 1.2 软件评审1.3 软件可靠性 2、软件测试2.1 软件测试过程模型软件测试策略软件测试策略V模型回归测试软件测试策略原则软件测试策…

JavaEE进阶---SpringMVC(二)请求里面十种参数类型

文章目录 1.请求1.1接受单个参数的请求1.2多个参数的传递1.3传递对象1.4参数重命名1.5设置参数是非必传的1.6数组的请求方式1.7如何传递集合1.8传递json数据1.9获取url里面的参数1.10获取文件 1.请求 1.1接受单个参数的请求 下面的这个就是我们的项目代码&#xff0c;都是单个…