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

如何高效将std::shared_ptr副本传入仅接受const void*的C函数/线程

将std::shared_ptr副本传入仅接受const void*参数的C函数/API/线程的最高效方法

要在仅接受void*参数的C风格接口中传递std::shared_ptr副本,核心需求是保证引用计数正确维护,同时尽量减少额外开销。你的现有方法可行,但可以从效率和安全性两方面优化。

现有方法的优缺点

你当前通过new std::shared_ptr<My1>创建堆上的副本,传入线程后再delete:

  • ✅ 逻辑简单,能正确增加引用计数,保证对象在线程处理期间存活
  • ❌ 多了一次不必要的堆分配(std::shared_ptr本身仅占2个指针大小,堆分配的开销相对其自身来说是额外负担)

更高效的优化方案

方案1:利用std::unique_ptr简化内存管理(安全优先,效率接近原方法)

用std::unique_ptr管理堆上的std::shared_ptr,避免手动delete,同时保持效率:

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

struct My1
{
    My1() { std::cout << "My1 constructor" << std::endl; }
    ~My1() { std::cout << "My1 destructor" << std::endl; }
};

void* thread1(void* pParam)
{
    // 用unique_ptr接管所有权,自动释放
    std::unique_ptr<std::shared_ptr<My1>> pp1(static_cast<std::shared_ptr<My1>*>(pParam));
    std::cout << "thread1 ref count: " << (*pp1).use_count() << std::endl;
    // 线程结束后,unique_ptr自动销毁堆上的shared_ptr,引用计数减1
    return nullptr;
}

int main()
{
    std::shared_ptr<My1> p1 = std::make_shared<My1>();
    std::cout << "ref count: " << p1.use_count() << std::endl;

    auto pp1 = std::make_unique<std::shared_ptr<My1>>(p1);
    pthread_t thr1;
    if (pthread_create(&thr1, nullptr, thread1, pp1.get()) != 0)
    {
        // 创建失败,unique_ptr自动释放堆内存
        std::cerr << "Failed to start thread" << std::endl;
    }
    else
    {
        // 创建成功,释放unique_ptr的所有权,让线程接管
        pp1.release();
    }

    std::cout << "ref count: " << p1.use_count() << std::endl;

    int i;
    std::cin >> i;
    return 0;
}

方案2:无额外堆分配(极致高效,但有前提)

如果主线程可以保证传递的std::shared_ptr对象在整个线程执行期间始终存活,可以直接传递栈上std::shared_ptr的地址,完全避免堆分配:

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

struct My1
{
    My1() { std::cout << "My1 constructor" << std::endl; }
    ~My1() { std::cout << "My1 destructor" << std::endl; }
};

void* thread1(void* pParam)
{
    std::shared_ptr<My1>& p1 = *static_cast<std::shared_ptr<My1>*>(pParam);
    std::cout << "thread1 ref count: " << p1.use_count() << std::endl;
    // 使用p1,注意主线程不能提前销毁p1
    return nullptr;
}

int main()
{
    std::shared_ptr<My1> p1 = std::make_shared<My1>();
    std::cout << "ref count: " << p1.use_count() << std::endl;

    pthread_t thr1;
    if (pthread_create(&thr1, nullptr, thread1, &p1) != 0)
    {
        std::cerr << "Failed to start thread" << std::endl;
    }
    else
    {
        // 必须等待线程结束,否则p1销毁后线程访问会出错
        pthread_join(thr1, nullptr);
    }

    std::cout << "ref count: " << p1.use_count() << std::endl;

    return 0;
}

⚠️ 注意:这个方案的局限性很强——主线程必须等待线程执行完毕,否则栈上的p1被销毁后,线程会访问悬空引用,导致未定义行为。

结论

  • 如果主线程无法等待线程结束,方案1是最优选择:既保留了原方法的正确性,又通过std::unique_ptr简化了内存管理,效率几乎和原方法一致(仅多了unique_ptr的构造/析构,开销可忽略)。
  • 如果主线程可以等待线程结束,方案2能实现极致高效,完全避免额外堆分配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 06:17:57