我有一个强大的用例,可以预先分配我需要的所有内存并在完成时释放它。
我已经推出了这个真正简单的缓冲池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);
}
};
答案 0 :(得分: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));
希望它有所帮助!
答案 1 :(得分:1)
我不明白为什么你要“包装”STL队列&lt;&gt;容器。只需将“缓冲区”放入队列中,然后根据需要提取地址。当您在缓冲区中完成“段”时,只需将其从队列中弹出即可自动释放。因此,您只需拥有实际的缓冲区类,而不是指向缓冲区的指针。
重新发明轮子让我感到震惊。既然你需要一次分配整个事件,我会使用vector
而不是队列,因为vector<>
类型可以在构造时一次性分配,而push_back()
方法不会除非需要,否则重新分配,与pop_back()
相同。有关使用的方法,请参阅here。
但基本上,这是我背后的想法:
#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
类型也可能值得一看,或者其他标准容器,但如果它只是“我只需要一个alloc或de-alloc”我只使用向量,甚至可能使用指向数组的智能指针
答案 2 :(得分:0)
我发现使用对象池的一些东西:
我不确定一次分配所有对象。我喜欢从'pooledObject'类中下载所有池化对象,该类包含对其自己的池的私有引用,因此允许一个简单的,无参数的'release'方法,我总是绝对确定一个对象总是被释放回它的自己的游泳池我不确定如何使用静态数组ctor加载池引用的每个实例 - 我总是在循环中逐个构造对象。
另一个有用的私有成员是一个'assigned'布尔值,当一个对象被释放并在释放时被清除时设置。这允许池类检测并且如果对象被释放两次则立即检测。如果没有立即检测到,“发布两次”错误可能会非常令人讨厌 - 奇怪的行为或崩溃发生在几分钟之后,通常是在另一个模块的另一个线程中。最好尽快检测双重发布!
我发现将我的池级别转储到1s计时器上的状态栏很有用并且让人放心。如果发生泄漏,我可以看到它发生,并且通常会在数字惊人地下降时通过我正在进行的活动了解泄漏的位置。谁需要Valgrind:)
关于线程的主题,如果必须使池成为线程安全的,那么使用阻塞队列会有所帮助。如果池用完,尝试获取对象的线程可以等到它们被释放并且应用程序只是减速而不是崩溃/死锁。另外,小心重新。虚假分享。您可能必须使用“填充”数组成员来确保没有两个对象共享缓存行。