当前位置:  开发笔记 > 编程语言 > 正文

非阻塞线程安全内存池实现

如何解决《非阻塞线程安全内存池实现》经验,为你挑选了1个好方法。

我需要一个简单的非阻塞静态块大小的内存池.我没有在网上找到这样的东西.所以每个人都需要这样的解决方案.这个是免费的...只适用于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.
template 
class 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都交换指针增加序列号,侧面就可以使用窄版本.



1> Ben Jackson..:

这种方法的问题在于:存在竞争条件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都交换指针增加序列号,侧面就可以使用窄版本.

推荐阅读
yzh148448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有