我需要一个简单的非阻塞静态块大小的内存池.我没有在网上找到这样的东西.所以每个人都需要这样的解决方案.这个是免费的...只适用于Win32.
最好的祝福,
弗里德里希
#ifndef MEMPOOL_HPP_INCLUDED #define MEMPOOL_HPP_INCLUDED #include "atomic.hpp" #include "static_assert.hpp" #pragma warning( push ) #pragma warning( disable : 4311 ) // warning C4311: 'Typumwandlung' /// @brief Block-free memory-pool implemenation /// @tparam T Object-type to be saved within the memory-pool. /// @tparam S Capacy of the memory-pool. templateclass MemoryPool { private: STATIC_ASSERT(sizeof(int) == sizeof(void*), "Well, ..."); public: /// @brief Object-type saved within the pool. typedef T TYPE; enum { /// @brief Capacy of the memory-pool. SIZE = S }; private: /// @brief Chunks, that holds the memory struct Chunk { /// @brief Single-linked list. Chunk* Next; /// @brief The value /// We do not call the default constructor this way. char Value[sizeof(TYPE)]; }; /// @brief The root object. Chunk* Root; /// @brief The pool Chunk Pool[SIZE]; private: // do not allow copying MemoryPool(const MemoryPool&); MemoryPool& operator=(const MemoryPool&); void free(Chunk* c) { c->Next = Root; while(!CompareAndSwap((int*)&Root, (int)c->Next, (int)c)) { c->Next = Root; } } public: /// Default constructor /// Creates an empty memory-pool. /// Invalidates all the memory. MemoryPool() : Root(0) { for(int i = 0; i < SIZE; i++) { MemoryPool::free(&Pool[i]); } } /// @brief Frees a chunk of memory, that was allocated by MemoryPool::malloc /// @param _Chunk A chunk of memory, that was allocated by MemoryPool::malloc /// This function will not call the destructor. /// Thread-safe, non-blocking void free(T* _Chunk) { if(!_Chunk) return; Chunk* c = (Chunk*)((int)(_Chunk) - sizeof(Chunk*)); if(c < &Pool[0] || c > &Pool[SIZE - 1]) return; MemoryPool::free(c); } /// @brief Returns a pointer to a chunk of memory /// @return 0 on a memory shortage /// @return A pointer to a chunk of memory /// This function will not call the constructor. /// Thread-safe, non-blocking T* malloc() { Chunk* r = Root; if(!r) return 0; while(!CompareAndSwap((int*)&Root, (int)r, (int)r->Next)) { r = Root; if(!r) return 0; } return &(r->Value); } }; #pragma warning( pop ) #endif // MEMPOOL_HPP_INCLUDED
还有CompareAndSwap
/// @brief Atomic compare and set /// Atomically compare the value stored at *p with cmpval and if the /// two values are equal, update the value of *p with newval. Returns /// zero if the compare failed, nonzero otherwise. /// @param p Pointer to the target /// @param cmpval Value as we excpect it /// @param newval New value static inline int CompareAndSwap(volatile int *_ptr, int _old, int _new) { __asm { mov eax, [_old] // place the value of _old to EAX mov ecx, [_new] // place the value of _new to ECX mov edx, [_ptr] // place the pointer of _ptr to EDX lock cmpxchg [edx], ecx // cmpxchg old (EAX) and *ptr ([EDX]) } return 1; }
Ben Jackson.. 9
这种方法的问题在于:存在竞争条件malloc
:
while(!CompareAndSwap((int*)&Root, (int)r, (int)r->Next))
请考虑以下操作序列:
原来 Root = A, A->next = B, ...
一个线程读取r = Root
,r = A
并且(读入寄存器)读取ecx = r->Next = B
初始线程被抢占(或者,在另一个CPU上)一系列malloc
并且free
发生这样的情况,这种A
情况在一段时间内被使用并且最后被释放.
新列表状态是 Root = A, A->next = ZZZ, ...
原始线程唤醒cmpxchg
并成功并成功Root == r == A
,因此设置Root = ecx = B
现在您的列表已损坏.
如果你有一个双字cmpxchg
,你可以解决这个问题,例如cmpxchg8b
.您只需在列表头旁边添加一个序列号,这样如果您在上面的(3)中被中断,则比较失败.free
只要每个malloc
都交换指针并增加序列号,侧面就可以使用窄版本.
这种方法的问题在于:存在竞争条件malloc
:
while(!CompareAndSwap((int*)&Root, (int)r, (int)r->Next))
请考虑以下操作序列:
原来 Root = A, A->next = B, ...
一个线程读取r = Root
,r = A
并且(读入寄存器)读取ecx = r->Next = B
初始线程被抢占(或者,在另一个CPU上)一系列malloc
并且free
发生这样的情况,这种A
情况在一段时间内被使用并且最后被释放.
新列表状态是 Root = A, A->next = ZZZ, ...
原始线程唤醒cmpxchg
并成功并成功Root == r == A
,因此设置Root = ecx = B
现在您的列表已损坏.
如果你有一个双字cmpxchg
,你可以解决这个问题,例如cmpxchg8b
.您只需在列表头旁边添加一个序列号,这样如果您在上面的(3)中被中断,则比较失败.free
只要每个malloc
都交换指针并增加序列号,侧面就可以使用窄版本.