zeromq源码学习——yqueue

来源:互联网 发布:淘宝买家售后申请退款 编辑:程序博客网 时间:2024/06/08 02:44

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

yqueue结构图

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<chunk_t> spare_chunk;    //禁止拷贝    yqueue_t (const yqueue_t&);    const yqueue_t &operator = (const yqueue_t&);};
原创粉丝点击