环形无锁队列

13 篇文章 0 订阅

无锁队列的优点如下

1、元素是先进先出的
由队列的性质保证的,在环形队列中通过对队列的顺序访问保证

2、空间可以重复利用
因为一般的环形队列都是一个元素数固定的一个闭环,可以在环形队列初始化的时候分配好确定的内存空间,当进队或出队时只需要返回指定元素内存空间的地址即可,这些内存空间可以重复利用,避免频繁内存分配和释放的开销

3、消息处理单线程化
最典型的生产者消费者模型中,如果引入环形队列,那么生成者只需要生成“东西”然后放到环形队列中即可,而消费者只需要从环形队列里取“东西”并且消费即可,没有任何锁或者等待,巧妙的高效实现了多线程数据通信。

这样处理没有了mutex,但效率较高。值得注意的是,当write和read相等时,表示满也表示队满也表示队空,这个队列是2个线程共享的,因此在这里是保证队空和队满时的线程安全,那么整个队列就是安全的,因为其他情况下read和write根本没有交集,当他们都在读写同一个index的时候,read线程先取走,再把标识置为false,write线程是先把内容写入完后再置true,这个读写和置false true的顺序一定不能乱否则就会标记位准备好了 。

#include <iostream>
#include <pthread.h>
#include <queue>
#include <strings.h>
#include <sstream>
#include <unistd.h>

using QueuePair = std::pair<unsigned int, unsigned char*>;
using CmdQueue = std::pair<volatile bool, QueuePair>;

class NonLockQueue
{
public:
    NonLockQueue():read(0), write(0), msg(NULL), msgLen(0)
    {}  

    ~NonLockQueue()
    {

    }   
    QueuePair *front()
    {
        if(cmdQueue[read].first)
            return &cmdQueue[read].second;
        return NULL;
    }

    void pop()
    {
        cmdQueue[read].first = false;
        read = (++read) % 1024;
    }

    bool push(const void *msg, const unsigned int len)
    {
        unsigned char* buf = new unsigned char[len];
        if(buf)
        {
            bcopy(msg, buf, len);
            if(!dump() && !cmdQueue[write].first)
            {
                cmdQueue[write].second.first = len;
                cmdQueue[write].second.second = buf;
                cmdQueue[write].first = true;
                write = (++write) % 1024;
                return true;
            }
            else
            {
                //如果队列满了,则写入缓冲
                cacheQueue.push(std::make_pair(len, buf));  
            }
            return true;
        }
        return false;
    }

private:

    bool dump()
    {
        //缓冲中还有数据
        while(!cacheQueue.empty())
        {
            if(cmdQueue[write].first)
            {
                return true;
            }
            // 优先将缓冲中的数据写入到队列
            cmdQueue[write].second = cacheQueue.front();
            cmdQueue[write].first = true;
            write = ++write % 1024;     
            cacheQueue.pop();
        }
        return false;
    }

    CmdQueue cmdQueue[1024];
    std::queue<QueuePair> cacheQueue;  
    unsigned int read;
    unsigned int write;
    unsigned char *msg;
    unsigned int msgLen;        
};

static void *funWrite(void *arg)
{
    NonLockQueue* pQue = (NonLockQueue*)arg;

    for(int i = 0; i < 1000; ++i)
    {
        std::stringstream ss;
        ss << i;
        pQue->push(ss.str().c_str(), ss.str().size() + 1);
    }
    return NULL;
}

static void* funRead(void *arg)
{
    NonLockQueue* pQue = (NonLockQueue*)arg;
    while(true)
    {
        QueuePair* pPair = pQue->front();   
        if(!pPair)
        {
            continue;
        }

        std::cout << pPair->first << "----" << pPair->second << std::endl;
        pQue->pop();
    }
    return NULL;
}

int main()
{
    NonLockQueue que;
    pthread_t t1, t2;
    pthread_create(&t1, NULL, funWrite, &que);
    pthread_create(&t2, NULL, funRead, &que);

    while(true)
    {

    }
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;   
}

部分结果如下:
这里写图片描述

不用标识的实现方法,这里空出一个单元来标识队满,这样的原则:队列中有元素则可读,队列满则写缓存

(write - read + MAX_SIZE) % MAX_SIZE >= 1 //队列中有元素
(write + 1) % MAX_SIZE == read //队列满

另外一种方法

#include <iostream>
#include <pthread.h>
#include <queue>
#include <strings.h>
#include <sstream>
#include <unistd.h>

using QueuePair = std::pair<unsigned int, unsigned char*>;
const unsigned int MAX_SIZE = 1024;

class NonLockQueue
{
    public:
        NonLockQueue():read(0), write(0) {}  

        ~NonLockQueue()
        {

        }   

        QueuePair *front()
        {
            if((write - read + MAX_SIZE) % MAX_SIZE >= 1)
                return &cmdQueue[read];
            return NULL;
        }

        void pop()
        {
            if((write - read + MAX_SIZE) % MAX_SIZE >= 1)
                read = (++read) % MAX_SIZE;
            else
                return;
        }

        bool push(const void *msg, const unsigned int len)
        {
            unsigned char* buf = new unsigned char[len];
            if(buf)
            {
                bcopy(msg, buf, len);
                if(!dump() && !((write + 1) % MAX_SIZE == read))
                {
                    cmdQueue[write].first = len;
                    cmdQueue[write].second = buf;
                    write = (++write) % MAX_SIZE;
                    return true;
                }
                else
                {
                    //如果队列满了,则写入缓冲
                    cacheQueue.push(std::make_pair(len, buf));  
                }
                return true;
            }
            return false;
        }

    private:

        bool dump()
        {
            //缓冲中还有数据
            while(!cacheQueue.empty())
            {
                if((write + 1) % MAX_SIZE == read)
                    return true;
                // 优先将缓冲中的数据写入到队列
                cmdQueue[write] = cacheQueue.front();
                write = ++write % MAX_SIZE;     
                cacheQueue.pop();
            }
            return false;
        }

        QueuePair cmdQueue[MAX_SIZE];
        std::queue<QueuePair> cacheQueue;  
        unsigned int read;
        unsigned int write;
};
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值