天气与日历 切换到窄版

 找回密码
 立即注册

QQ登录

只需一步,快速开始

此广告位出租
查看: 400|回复: 0

[C++教程] C++环形无锁队列

[复制链接]

3188

主题

4

回帖

3290

积分

管理员

积分
3290
发表于 2024-2-26 12:43:39 | 显示全部楼层 |阅读模式
无锁队列的优点如下
1、元素是先进先出的
由队列的性质保证的,在环形队列中通过对队列的顺序访问保证

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


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


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

  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <queue>
  4. #include <strings.h>
  5. #include <sstream>
  6. #include <unistd.h>

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

  9. class NonLockQueue
  10. {
  11. public:
  12.     NonLockQueue():read(0), write(0), msg(NULL), msgLen(0)
  13.     {}  

  14.     ~NonLockQueue()
  15.     {

  16.     }   
  17.     QueuePair *front()
  18.     {
  19.         if(cmdQueue[read].first)
  20.             return &cmdQueue[read].second;
  21.         return NULL;
  22.     }

  23.     void pop()
  24.     {
  25.         cmdQueue[read].first = false;
  26.         read = (++read) % 1024;
  27.     }

  28.     bool push(const void *msg, const unsigned int len)
  29.     {
  30.         unsigned char* buf = new unsigned char[len];
  31.         if(buf)
  32.         {
  33.             bcopy(msg, buf, len);
  34.             if(!dump() && !cmdQueue[write].first)
  35.             {
  36.                 cmdQueue[write].second.first = len;
  37.                 cmdQueue[write].second.second = buf;
  38.                 cmdQueue[write].first = true;
  39.                 write = (++write) % 1024;
  40.                 return true;
  41.             }
  42.             else
  43.             {
  44.                 //如果队列满了,则写入缓冲
  45.                 cacheQueue.push(std::make_pair(len, buf));  
  46.             }
  47.             return true;
  48.         }
  49.         return false;
  50.     }

  51. private:

  52.     bool dump()
  53.     {
  54.         //缓冲中还有数据
  55.         while(!cacheQueue.empty())
  56.         {
  57.             if(cmdQueue[write].first)
  58.             {
  59.                 return true;
  60.             }
  61.             // 优先将缓冲中的数据写入到队列
  62.             cmdQueue[write].second = cacheQueue.front();
  63.             cmdQueue[write].first = true;
  64.             write = ++write % 1024;     
  65.             cacheQueue.pop();
  66.         }
  67.         return false;
  68.     }

  69.     CmdQueue cmdQueue[1024];
  70.     std::queue<QueuePair> cacheQueue;  
  71.     unsigned int read;
  72.     unsigned int write;
  73.     unsigned char *msg;
  74.     unsigned int msgLen;        
  75. };

  76. static void *funWrite(void *arg)
  77. {
  78.     NonLockQueue* pQue = (NonLockQueue*)arg;

  79.     for(int i = 0; i < 1000; ++i)
  80.     {
  81.         std::stringstream ss;
  82.         ss << i;
  83.         pQue->push(ss.str().c_str(), ss.str().size() + 1);
  84.     }
  85.     return NULL;
  86. }

  87. static void* funRead(void *arg)
  88. {
  89.     NonLockQueue* pQue = (NonLockQueue*)arg;
  90.     while(true)
  91.     {
  92.         QueuePair* pPair = pQue->front();   
  93.         if(!pPair)
  94.         {
  95.             continue;
  96.         }

  97.         std::cout << pPair->first << "----" << pPair->second << std::endl;
  98.         pQue->pop();
  99.     }
  100.     return NULL;
  101. }

  102. int main()
  103. {
  104.     NonLockQueue que;
  105.     pthread_t t1, t2;
  106.     pthread_create(&t1, NULL, funWrite, &que);
  107.     pthread_create(&t2, NULL, funRead, &que);

  108.     while(true)
  109.     {

  110.     }
  111.     pthread_join(t1, NULL);
  112.     pthread_join(t2, NULL);
  113.     return 0;   
  114. }
复制代码
部分结果如下:



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

  1. (write - read + MAX_SIZE) % MAX_SIZE >= 1 //队列中有元素
  2. (write + 1) % MAX_SIZE == read //队列满
复制代码
另外一种方法

  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <queue>
  4. #include <strings.h>
  5. #include <sstream>
  6. #include <unistd.h>

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

  9. class NonLockQueue
  10. {
  11.     public:
  12.         NonLockQueue():read(0), write(0) {}  

  13.         ~NonLockQueue()
  14.         {

  15.         }   

  16.         QueuePair *front()
  17.         {
  18.             if((write - read + MAX_SIZE) % MAX_SIZE >= 1)
  19.                 return &cmdQueue[read];
  20.             return NULL;
  21.         }

  22.         void pop()
  23.         {
  24.             if((write - read + MAX_SIZE) % MAX_SIZE >= 1)
  25.                 read = (++read) % MAX_SIZE;
  26.             else
  27.                 return;
  28.         }

  29.         bool push(const void *msg, const unsigned int len)
  30.         {
  31.             unsigned char* buf = new unsigned char[len];
  32.             if(buf)
  33.             {
  34.                 bcopy(msg, buf, len);
  35.                 if(!dump() && !((write + 1) % MAX_SIZE == read))
  36.                 {
  37.                     cmdQueue[write].first = len;
  38.                     cmdQueue[write].second = buf;
  39.                     write = (++write) % MAX_SIZE;
  40.                     return true;
  41.                 }
  42.                 else
  43.                 {
  44.                     //如果队列满了,则写入缓冲
  45.                     cacheQueue.push(std::make_pair(len, buf));  
  46.                 }
  47.                 return true;
  48.             }
  49.             return false;
  50.         }

  51.     private:

  52.         bool dump()
  53.         {
  54.             //缓冲中还有数据
  55.             while(!cacheQueue.empty())
  56.             {
  57.                 if((write + 1) % MAX_SIZE == read)
  58.                     return true;
  59.                 // 优先将缓冲中的数据写入到队列
  60.                 cmdQueue[write] = cacheQueue.front();
  61.                 write = ++write % MAX_SIZE;     
  62.                 cacheQueue.pop();
  63.             }
  64.             return false;
  65.         }

  66.         QueuePair cmdQueue[MAX_SIZE];
  67.         std::queue<QueuePair> cacheQueue;  
  68.         unsigned int read;
  69.         unsigned int write;
  70. };
复制代码

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员5折;永久VIP免费
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
快速回复 返回顶部 返回列表