2

我有一个强大的用例来预先分配我需要的所有内存并在完成后释放它。

我提出了这个真正简单的缓冲池 C++ 实现,我必须对其进行测试,但我不确定我尝试使用的指针算法是否允许我这样做。基本上是我接下来要做的事情并发布。我更喜欢这个想法的一些技巧,而不是依赖任何类型的内存处理程序,这只会使客户端代码更加复杂。

#include <stdio.h>
#include <queue>

#include "utils_mem.h"

using namespace std;

template <class T>
class tbufferpool {
private:
    const int m_initial;
    const int m_size;
    const int m_total;
    T*        m_buffer;
    vector<T*> m_queue;

public:
    // constructor
    tbufferpool(int initial, int size) : m_initial(initial), m_size(size), m_total(initial*size*sizeof(T)) {
        m_buffer = (T*) malloc(m_total);
        T* next_buffer = m_buffer;
        for (int i=0; i < initial; ++i, next_buffer += i*size) {
            m_queue.push_back(next_buffer);
        }
    }

    // get next buffer element from the pool
    T* next() {
        // check for pool overflow
        if (m_queue.empty()) {
            printf("Illegal bufferpool state, our bufferpool has %d buffers only.", m_initial);
            exit(EXIT_FAILURE);
        }
        T* next_buffer = m_queue.back();
        m_queue.pop_back(); 
        return next_buffer;
    }

    // release element, make it available back in the pool
    void release(T* buffer) {
        assert(m_buffer <= buffer && buffer < (buffer + m_total/sizeof(T)));
        m_queue.push_back(buffer);
    }

    void ensure_size(int size) {
        if (size >= m_size) {
            printf("Illegal bufferpool state, maximum buffer size is %d.", m_size);
            exit(EXIT_FAILURE);
        }
    }

    // destructor
    virtual ~tbufferpool() {
        free(m_buffer);
    }
};
4

3 回答 3

1

首先,当你增加一个指向 T 的指针时,它将指向内存中 T 的下一个元素。

m_queue.push(m_buffer + (i*size*sizeof(T)));

这应该像

m_buffer = (T*) malloc(m_total);
T* next = m_buffer;
for (int i=0; i < initial; ++i) {
    m_queue.push(next++);
}

第二,

assert(m_buffer <= buffer && buffer < m_total);

它应该像

assert(m_buffer <= buffer && buffer <= m_buffer + m_total/sizeof(T));

希望能帮助到你!

于 2012-04-05T19:55:16.633 回答
1

我不明白你为什么要“包装” STL queue<> 容器。只需将您的“缓冲区”放入队列中,然后根据需要提取地址。当您完成缓冲区中的“段”时,只需将其从队列中弹出并自动释放。因此,您只需拥有实际的缓冲区类,而不是指向缓冲区的指针。

它只是让我觉得重新发明了轮子。现在,由于您需要一次分配整个事物,因此我将使用vectornot queue,因为vector<>可以在构造时立即分配所有类型,并且push_back()除非需要,否则该方法不会重新分配,与pop_back(). 有关使用的方法,请参见此处。

不过,基本上,这是我粗略的想法:

#include <myType.h> // Defines BufferType

const int NUMBUFFERS = 30;

int main()
{
    vector<BufferType> myBuffers(NUMBUFFERS);
    BufferType* segment = &(myBuffers[0]);    // Gets first segment
    myBuffers.pop_back(); // Reduces size by one

    return 0;
}

我希望这能给你一个大致的想法。您可以这样使用向量中的缓冲区,并且只有一次分配或取消分配,如果您愿意,您可以使用类似堆栈的逻辑。该dequeue类型也可能值得一看,或者其他标准容器,但如果它只是“我只想要一个分配或解除分配”,我只会使用向量,甚至可能使用指向数组的智能指针。

于 2012-04-05T19:59:40.033 回答
0

我使用对象池发现的一些东西:

我不确定一次分配所有对象。我喜欢从包含对其自己池的私有引用的“pooledObject”类中继承所有池对象,因此允许使用简单、无参数的“释放”方法,并且我始终绝对确定一个对象总是被释放回它的自己的游泳池。我不确定如何使用静态数组 ctor 使用池引用加载每个实例 - 我总是在循环中一个接一个地构造对象。

另一个有用的私有成员是“已分配”布尔值,在对象被解池时设置并在释放时清除。这允许池类检测对象是否被释放两次,并且立即除外。如果不立即检测到“两次发布”错误,可能会非常讨厌——奇怪的行为或崩溃会在几分钟后发生,而且通常发生在另一个模块的另一个线程中。最好尽快检测双重释放!

我发现将我的池级别转储到 1 秒计时器上的状态栏是有用且令人放心的。如果发生泄漏,我可以看到它正在发生,并且通常,当数字急剧下降时,我可以通过我正在进行的活动来了解泄漏的位置。谁需要 Valgrind:)

关于线程,如果您必须使池线程安全,则使用阻塞队列会有所帮助。如果池用完,试图获取对象的线程可以等到它们被释放,应用程序就会变慢而不是崩溃/死锁。另外,要小心重新。虚假分享。您可能必须使用“填充”数组数据成员来确保没有两个对象共享缓存行。

于 2012-04-05T23:37:02.313 回答