有关内存泄漏的帮助 - 具有多线程队列,字符缓冲区和结构

时间:2010-11-13 16:21:30

标签: c++ multithreading boost memory-leaks queue

所以我有一个提升的队列类,可以帮助多线程描述here

在我的班级声明中我有

//...
    struct VideoSample
    {   
        const unsigned char * buffer;
        int len;
    };

    ConcurrentQueue<VideoSample * > VideoSamples;

//...

struct AudioSample
{   
    const unsigned char * buffer;
    int len;
};

ConcurrentQueue<AudioSample * > AudioSamples;

//...

在我班上我有一个功能:

void VideoEncoder::AddFrameToQueue(const unsigned char *buf, int size )
{
    VideoSample * newVideoSample = new VideoSample;
    VideoSamples.try_pop(newVideoSample);

    newVideoSample->buffer = buf;
    newVideoSample->len = size;
    VideoSamples.push(newVideoSample);
    //free(newVideoSample->buffer);
    //delete newVideoSample;
}

我的应用程序只需要在队列中保留一个帧。

提供here关于如何删除结构的答案在这种情况下没有帮助,因为app粉碎了。

我有类似的音频队列代码。

void VideoEncoder::AddSampleToQueue(const unsigned char *buf, int size )
{
    AudioSample * newAudioSample = new AudioSample;
    newAudioSample->buffer = buf;
    newAudioSample->len = size;
    AudioSamples.push(newAudioSample);
    url_write (url_context, (unsigned char *)newAudioSample->buffer, newAudioSample->len);
    AudioSamples.wait_and_pop(newAudioSample);
    //delete newAudioSample;
}

和一个在单独的线程中运行的函数:

void VideoEncoder::UrlWriteData()
{
    while(1){
        switch (AudioSamples.empty()){
        case true : 
            switch(VideoSamples.empty()){
        case true : Sleep(5); break;
        case false :    
            VideoSample * newVideoSample = new VideoSample;
            VideoSamples.wait_and_pop(newVideoSample);
            url_write (url_context, (unsigned char *)newVideoSample->buffer, newVideoSample->len);
            break;
            } break;
        case false :  Sleep(3);     break;
        }
    }
}

顺便说一句:要将媒体数据流式传输到网址,我使用了ffmpeg的功能。

BTW:这里我用于队列的代码:

#include <string>
#include <queue>
#include <iostream>

// Boost
#include <boost/thread.hpp>
#include <boost/timer.hpp>

template<typename Data>
class ConcurrentQueue
{
private:
    std::queue<Data> the_queue;
    mutable boost::mutex the_mutex;
    boost::condition_variable the_condition_variable;
public:
    void push(Data const& data)
    {
        boost::mutex::scoped_lock lock(the_mutex);
        the_queue.push(data);
        lock.unlock();
        the_condition_variable.notify_one();
    }

    bool empty() const
    {
        boost::mutex::scoped_lock lock(the_mutex);
        return the_queue.empty();
    }

    bool try_pop(Data& popped_value)
    {
        boost::mutex::scoped_lock lock(the_mutex);
        if(the_queue.empty())
        {
            return false;
        }

        popped_value=the_queue.front();
        the_queue.pop();
        return true;
    }

    void wait_and_pop(Data& popped_value)
    {
        boost::mutex::scoped_lock lock(the_mutex);
        while(the_queue.empty())
        {
            the_condition_variable.wait(lock);
        }

        popped_value=the_queue.front();
        the_queue.pop();
    }

    Data& front()
    {
        boost::mutex::scoped_lock lock(the_mutex);
        return the_queue.front();
    }

};

我的问题是:如何清理AddSampleToQueue和AddFrameToQueue以免它们造成内存泄漏?

BTW:我对所有这些C ++共享/自动内容都很陌生。所以说一个初学者。所以请提供至少可以集成到我的代码中的代码示例 - 因为我提供了所有代码。因此,如果您知道该怎么做 - 请尝试将您的知识整合到我的示例中。谢谢。如果你能告诉我如何不使用共享/自动ptrs,我会非常高兴。

9 个答案:

答案 0 :(得分:3)

使用智能指针:http://www.drdobbs.com/184401507

答案 1 :(得分:3)

使用Shared_ptr

答案 2 :(得分:3)

如果,当帧被添加到队列中时,数据数组的所有权将转移到样本,free或者删除样本的析构函数中的[]。

您也可能希望使用移动构造函数,这样您就可以拥有ConcurrentQueue<VideoSample>而不是ConcurrentQueue<VideoSample*>的队列,这样可以使您自动排队和出列的样本。

或者,如果您控制将数据推送到队列的内容,请使用vector或boost :: array而不是C样式数组。

如果你真的只想要一件东西,那么使用队列也有点奇怪。拥有一个受互斥锁和条件变量保护的变量就可以了。

答案 3 :(得分:3)

很多其他人都提出了共享指针 我没有看到在这里不使用队列的共享指针而是的原因。毕竟,你只允许一帧。它们优雅地支持锁定,只需要一点点躲避就可以使线程安全且简单。你真的没有机会发现我可以看到的循环引用,所以你应该基本上这样。

或者,这听起来像一个漂亮的循环缓冲区的工作。这样你就可以完全避免裸char数组。 Boost优雅地实现了一个,只需要一点原始同步,你就应该能够使它适合你的目的。特别值得注意的是,这将使扩展您的应用程序以处理在线数据处理也相对简单。

如果您有兴趣,我会破解一些示例代码。

答案 4 :(得分:1)

首先,我会将ConcurrentQueue<VideoSample * > VideoSamples; 更改为

ConcurrentQueue<VideoSample> VideoSamples;

您不需要此指针。把剩下的指针转向智能指针,你就完全了!

答案 5 :(得分:1)

valgrind将帮助您找到程序中几乎任何内存泄漏。虽然正如其他人指出的那样,你应该使用shared_ptrs。

答案 6 :(得分:1)

您的代码

VideoSample * newVideoSample = new VideoSample;
VideoSamples.try_pop(newVideoSample);

是内存泄漏。如果try_pop成功,它将覆盖newVideoSample中的指针,并且您之前创建的实例的引用将永远丢失!

答案 7 :(得分:1)

将您的功能更改为以下功能,并在您分配内存的其他位置执行类似的更改。

void VideoEncoder :: AddFrameToQueue(const unsigned char * buf,int size) {

VideoSample * newVideoSample;
if(!VideoSamples.try_pop(newVideoSample))
{
    newVideoSample  = new VideoSample; 
}
else
{
   delete buff;
}

newVideoSample->buffer = buf; 
newVideoSample->len = size; 
VideoSamples.push(newVideoSample); 

}

我也不能阻止自己提出这个问题。当你只想要一个项目在队列中时,为什么你需要排队呢。

答案 8 :(得分:1)

我不确定我是否理解这一切,但无论如何我都会试一试。

AddFrameToQueue函数

所以显然你一次想要队列中的一个帧,这意味着你可能根本不需要队列。无论如何:要么队列中没有帧,你应该创建一个新帧,或者有,你应该覆盖它的bufferlen字段:

void VideoEncoder::AddFrameToQueue(const unsigned char *buf, int size )
{
    VideoSample * newVideoSample = 0;
    if (!VideoSamples.try_pop(newVideoSample))
    {
        // Nothing in queue yet : we allocate a whole new VideoSample
        newVideoSample = new VideoSample;
    }
    else
    {
        // Here, you want to release newVideoSample->buffer depending on
        // the way it was allocated in the first place : free if malloc'ed,
        // delete if new'ed...
    }

    newVideoSample->buffer = buf;
    newVideoSample->len = size;
    VideoSamples.push(newVideoSample);

    // The VideoSample pointer has been pushed in the queue : we must no delete
    // it in order for the queue to keep containing a valid pointer
}

AddSampleToQueue函数

为什么在这个函数的末尾有一个wait_and_pop:不是应该在UrlWriteData中发生的流行音乐?我真的不明白这一部分。如果目标是在队列中有一个项目,你可能不需要队列(第2集),但我想你可以使用与AddFrameToQueue相同的代码。

UrlWriteData函数

此处,数据实际上已从队列中删除,因此您希望在完成编写后立即将其释放。

void VideoEncoder::UrlWriteData()
{
    while(1){
        switch (AudioSamples.empty()){
        case true : 
            switch(VideoSamples.empty()){
            case true : Sleep(5); break;
            case false :    
                VideoSample * newVideoSample;
                VideoSamples.wait_and_pop(newVideoSample);
                url_write (url_context, (unsigned char *)newVideoSample->buffer, newVideoSample->len);

                // Release newVideoSample->buffer using free if malloc'ed, delete
                // if new'ed...

                delete newVideoSample;

                break;
            } break;
        case false :  Sleep(3);     break;
        }
    }
}

这是我无法告诉你的最好的东西,不会破坏整个事情,去寻找智能指针,RAII以及所有那些使C ++成为现实的东西:)