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

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.kler.cn/a/393195.html

相关文章:

  • kafka面试题解答(四)
  • WebRTC API分析
  • 计算机毕业设计Python+Neo4j知识图谱医疗问答系统 大模型 机器学习 深度学习 人工智能 大数据毕业设计 Python爬虫 Python毕业设计
  • 万字长文分析函数式编程
  • Linux kernel 堆溢出利用方法(二)
  • 软件测试面试2024最新热点问题
  • GxtWaitCursor:Qt下基于RAII的鼠标等待光标类
  • Spring Boot 自动装配原理
  • C++20 STL CookBook 7 Containers(II)
  • Elman 神经网络算法详解
  • 详解kafka消息发送重试机制的案例
  • Threejs 材质贴图、光照和投影详解
  • Redis增删改查、复杂查询案例分析
  • 【计算机网络】【网络层】【习题】
  • 网络安全——应急响应之Linux入侵排查
  • 2024 年 8 个最佳 API 设计工具图文介绍
  • Java开发人员从零学习ArkTs笔记(二)-函数与类
  • Ubuntu20.04 为脚本文件创建桌面快捷方式 ubuntu
  • Spring Boot框架:电商开发的新趋势
  • #渗透测试#SRC漏洞挖掘#云技术基础03之容器相关
  • 如何用Chrome的Network面板分析HTTP报文
  • 壁仞科技上市前最后一波 校招 社招 内推
  • 基于Java Springboot在线教育学习系统
  • 卫导调零天线功率倒置算法原理及MATLAB仿真
  • 【paper】分布式无人水下航行器围捕智能目标
  • ONLYOFFICE 8.2深度测评:集成PDF编辑、数据可视化与AI功能的强大办公套件