shared_ptr不在线程中释放自定义malloc

时间:2015-07-03 07:14:55

标签: c++ lambda c++14

在我的代码中,我在lambda中创建一个shared_ptr,以便将PNG文件保存为后台任务。不幸的是,即使我有shared_ptr的自定义删除器,似乎没有正确释放字节。

我用来创建shared_ptr的代码:

std::shared_ptr<GLubyte> buffer = std::shared_ptr<GLubyte>((GLubyte*) malloc(*dataLength), [](GLubyte* buffer) {
        free(buffer);
    });

为了保存文件并最终解除分配:

std::thread t([=, buffer = std::move(buffer)]() mutable {
        bool done = writePNGFileFromBuffer(path, buffer.get(), width, height);
        return done;
    });
t.detach();

我试图将buffer.reset()放在lambda中,但是虽然缓冲区为空,但内存不会被释放。我也尝试将创建者功能更改为:

std::shared_ptr<GLubyte> buffer = std::shared_ptr<GLubyte>((GLubyte*) malloc(*dataLength), std::free);

但它也行不通。现在我一直在使用lambda删除器,因为我可以尝试在内部放置一个断点并检查free是否被调用,但内存仍未释放。

此外,我已经验证,如果我将free(buffer.get())放在lambda中,则该版本有效,但是对我来说这是没有意义的,因为我正在使用shared_ptr以避免像此

你能帮我发布这个缓冲区吗?非常感谢你。

1 个答案:

答案 0 :(得分:1)

我写了这个小测试工具来证明新的/删除是正确的。

注意在缓冲区构造函数中使用new / delete []。你使用malloc / free会给代码带来难闻的气味。求助于自由(ptr.get())隐藏了一些你尚未解决的逻辑问题。如果你把它留在程序中,它会在以后咬你。

proxy作为GLubyte的替代品,计算分配和销毁的数量,因此我可以确认assert每个构造都有相应的破坏。

#include <iostream>
#include <memory>
#include <thread>
#include <cassert>
#include <atomic>

using namespace std;

#define USE_PROXY 1

struct proxy
{
    proxy() {
        ++_count;
    }
    ~proxy() {
        --_count;
    }

    static std::atomic<size_t> _count;
};
std::atomic<size_t> proxy::_count = { 0 };

#if USE_PROXY
using GLubyte = proxy;
#else
//using GLubyte = uint8_t;
#endif

bool writePNGFileFromBuffer(const char* path, const GLubyte* bytes, int width, int height)
{

    return true;
}


auto main() -> int
{
    {
        int dataLength = 10000;

        auto buffer = std::shared_ptr<GLubyte>(new GLubyte[dataLength], 
                                               [](GLubyte* p) { delete[] p; });

        const char* path = "/tmp/foo";
        int width = 100, height = 100;

        std::thread t([=, buffer = std::move(buffer)]() mutable {
            bool done = writePNGFileFromBuffer(path, buffer.get(), width, height);
            return done;
        });
        t.join();
        assert(!buffer);
    }
    assert(proxy::_count == 0);
    return 0;
}

为了进一步考虑,您可能想知道如何处理write....函数中的失败案例。目前,返回值正在被抛弃。我们有几种方法可以解决这个问题 - 一种方法是提供一个可以在写操作完成时调用的lambda。另一种方法是将写操作包装成std :: async。

bool writeSharedPNGFileFromBuffer(const char* path, shared_ptr<const GLubyte> bytes, int width, int height)
{
    return writePNGFileFromBuffer(path, bytes.get(), width, height);
}


auto main() -> int
{
    {
        int dataLength = 100;

        auto buffer = std::shared_ptr<GLubyte>(new GLubyte[10000], [](GLubyte* p) { delete[] p; });

        const char* path = "/tmp/foo";
        int width = 100, height = 100;

        auto f = std::async(launch::async, writeSharedPNGFileFromBuffer, path, move(buffer), width, height);
        // f is a std::future - a handle to somewhere the result will eventually land.
        // perform main thread work here...
        // ... and get the return value when we're ready to deal with it
        auto written = f.get();
        cout << "written: " << written << endl;
        assert(!buffer);
    }
    assert(proxy::_count == 0);
    return 0;
}