You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QNX系统pthread_cancel致内存泄漏,Linux无此异常

QNX与Linux下pthread_cancel导致对象析构行为差异

我编写了一段测试代码:主线程创建thread_1和thread_2两个线程,在thread_2中调用pthread_cancel取消thread_1。在QNX系统运行时,thread_1中创建的MyClass和MyClass2对象析构函数未被调用,导致100M内存泄漏;但在Linux系统中运行时,对象析构函数会正常执行。多次验证代码无问题,想了解两者存在差异的原因。

测试代码如下:

#include <iostream>
#include <pthread.h>
#include <thread>

using namespace std;

pthread_t thread_id_1;
pthread_t thread_id_2;

class MyClass2
{
public:
    MyClass2() {
        cout << "Build MyClass2" << endl;
    }
    ~MyClass2() {
        cout << "Destory MyClass2" << endl;
    }
};

class MyClass
{
public:
    MyClass() {
        cout << "Build MyClass" << endl;
        p = (char *)malloc(1024 * 1024 *100);
    }
    ~MyClass() {
        cout << "Destory MyClass" << endl;
        free(p);
    }
    char *p;
};

void func(int i)
{
    MyClass2 c2;
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    cout << "thread 1 func:" << i << endl;
}

static void *thread_1(void *arg)
{
    MyClass my_class;
    int type_value = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    cout << "thread_1 set cancle type+++++:" << type_value << endl;
    for (int i = 0; i < 10; i++) {
        func(i);
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
    return nullptr;
}

static void *thread_2(void *arg)
{
    for (int i = 0; i < 10; i++) {
        cout << "thread_2:" << i << endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    int ret = pthread_cancel(thread_id_1);
    cout << "otx_thread_2 cancel thread 1 ret:" << ret << endl;
    return nullptr;
}

int main(int argc, char *argv[])
{
    cout << "Main start" << endl;

    pthread_attr_t attr;
    pthread_attr_init( &attr );
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    cout << "Main set detch" << endl;
    if (pthread_create(&thread_id_1, &attr, thread_1, nullptr) != 0) {
        cout << "pthread_create() 1 error" << endl;
        return -1;
    }
    if (pthread_create(&thread_id_2, nullptr, thread_2, nullptr) != 0) {
        cout << "pthread_create() 2 error" << endl;
        return -1;
    }
    if (pthread_join(thread_id_2, NULL) != 0) {
        cout << "pthread_join() 1 error";
        return -1;
    }
    while (1) {
        cout << "Main Loop" << endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
    return 0;
}

差异原因分析

  • 异步取消模式的实现差异:你在thread_1中设置了PTHREAD_CANCEL_ASYNCHRONOUS(异步取消),该模式允许线程在任意指令点被终止。

    • Linux的NPTL线程库在处理异步取消时,会触发栈展开流程,自动调用线程栈上所有局部对象的析构函数,完成资源清理。
    • QNX的线程实现对异步取消的处理更为直接,直接终止线程执行,不会触发栈展开,因此局部对象的析构函数无法被调用,malloc分配的内存也就无法释放,导致泄漏。
  • POSIX标准的灵活性:POSIX标准并未强制要求异步取消时必须执行栈展开,仅规定了在取消点(如sleep、read等系统调用)处响应取消时需要处理资源清理。这就导致不同操作系统的线程库可以根据自身设计选择不同的处理逻辑。

  • 取消模式的影响:如果使用默认的PTHREAD_CANCEL_DEFERRED(延迟取消),线程只会在POSIX定义的取消点处响应取消,此时系统会自动执行栈展开和析构函数。你使用异步取消跳过了这一标准流程,直接暴露了两个系统实现的差异。

解决方案建议

  • 优先使用延迟取消模式(默认模式),避免异步取消,确保线程在取消点处响应取消,让系统自动完成资源清理。
  • 如果必须使用异步取消,需手动通过pthread_cleanup_push/pthread_cleanup_pop注册清理函数,在线程被取消时主动调用对象析构、释放内存等资源。

内容的提问来源于stack exchange,提问作者Allen_zhao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 19:35:16