zeromq源码学习——yqueue

yqueue是一个高效的队列实现,能够减少内存分配和释放的次数。yqueue可以在一个线程中调用push/back函数,在另一个线程中调用pop/front函数。但是要确保不会pop一个空队列,并且两个线程不能在无锁条件下访问同一个元素。

zeromq源码学习——yqueue_第1张图片

yqueue中为一个chunk_t类型的双向链表,每个chunk里保存有N个元素。pos表示当前chunk中所指向的有效元素位置,通过增加pos可以实现快速的push/pop操作。back_chunk和end_chunk的区别为:back_chunk指向最后一个有效的元素,end_chunk指向下一个push位置。

yqueue的操作没有做有效性检查,一切操作需要调用者确保安全性。

yqueue源码:

//T是队列中对象类型
//N是队列粒度,用来减少内存分配和释放次数
template <typename T, int N> class yqueue_t
{
public:
    //构造函数
    inline yqueue_t ()
    {
         begin_chunk = allocate_chunk();
         alloc_assert (begin_chunk);
         begin_pos = 0;
         back_chunk = NULL;
         back_pos = 0;
         end_chunk = begin_chunk;
         end_pos = 0;
    }

    //析构函数
    inline ~yqueue_t ()
    {
        while (true) {
            if (begin_chunk == end_chunk) {
                free (begin_chunk);
                break;
            } 
            chunk_t *o = begin_chunk;
            begin_chunk = begin_chunk->next;
            free (o);
        }

        chunk_t *sc = spare_chunk.xchg (NULL);
        free (sc);
    }

    //返回队列第一个元素,队列为空,则行为未定义
    inline T &front ()
    {
         return begin_chunk->values [begin_pos];
    }

    //返回队列最后一个元素,队列为空,则行为未定义
    inline T &back ()
    {
        return back_chunk->values [back_pos];
    }

    //从队尾压入元素
    inline void push ()
    {
        back_chunk = end_chunk;
        back_pos = end_pos;

        if (++end_pos != N)
            return;

        chunk_t *sc = spare_chunk.xchg (NULL);
        if (sc) {
            end_chunk->next = sc;
            sc->prev = end_chunk;
        } else {
            end_chunk->next = allocate_chunk();
            alloc_assert (end_chunk->next);
            end_chunk->next->prev = end_chunk;
        }
        end_chunk = end_chunk->next;
        end_pos = 0;
    }

    //回滚上次压入操作。
    //注:调用者需要负责销毁unpush对象。调用者要确保unpush时队列非空。该调用是非线程安全的。
    inline void unpush ()
    {
        //首先,移除最后一个位置的元素
        if (back_pos)
            --back_pos;
        else {
            back_pos = N - 1;
            back_chunk = back_chunk->prev;
        }

        if (end_pos)
            --end_pos;
        else {
            end_pos = N - 1;
            end_chunk = end_chunk->prev;
            free (end_chunk->next);
            end_chunk->next = NULL;
        }
    }

    //队尾移除元素
    inline void pop ()
    {
        //++begin_pos==N,表示第一个内存块用尽
        if (++ begin_pos == N) {
            chunk_t *o = begin_chunk;
            begin_chunk = begin_chunk->next;
            begin_chunk->prev = NULL;
            begin_pos = 0;
            //保存最近一次释放的内存块
            chunk_t *cs = spare_chunk.xchg (o);
            free (cs);
        }
    }

private:

    //持有N个元素的内存块,实现为一个双向队列
    struct chunk_t
    {
         T values [N];
         chunk_t *prev;
         chunk_t *next;
    };

    //内存块分配函数
    inline chunck_t *allocate_chunk()
    {
        //省略内存定义内存对齐时的调用
        return (chunck_t*) malloc(sizeof(chunck_t));
    }

    chunk_t *begin_chunk;//第一个内存块
    int begin_pos;
    chunk_t *back_chunk;//指向最后一个有效元素块
    int back_pos;
    chunk_t *end_chunk;//指向下一个可压入元素块
    int end_pos;

    //大多数情况下,生产和消费频率相近,因此保存最近一次释放的内存块(从queue中去除,但内存未释放),减少内存释放和分配频率
    atomic_ptr_t spare_chunk;

    //禁止拷贝
    yqueue_t (const yqueue_t&);
    const yqueue_t &operator = (const yqueue_t&);
};

你可能感兴趣的:(zeromq)