基础组件(五):无锁消息队列


一、无锁队列实现

1. 什么是无锁队列?

使用队列的数据结构,同时用无锁的技术来锁定这些队列的操作,实现的队列叫做无锁队列。

无锁原语(lock free | wait free | blocking)

无锁 :不能使用互斥锁,只能基于原子操作,内存屏障等实现。

  • lock free(无锁) : 至少保证一个线程向前移动
    系统作为一个整体无论如何都向前移动,但不能保证每个线程的前进进度(可能出现一个线程移动,其他线程饿死的状态)。通常使用compare_exchange(CAS)原语实现。允许存在循环,但不能使用类似compare_exchange实现的自旋锁(可能造成死锁)
  • wait free(无等待) :保证所有线程向前移动
    保证每个线程都能在有限步骤内向前移动,不受其他线程争用或阻塞的影响。使用exchange、fetch_add等原子操作原语实现。不允许包含可能被其他线程影响的循环。
  • blocking(阻塞) :不保证所有线程向前移动
    整个系统可能不会取得任何进展,阻塞、中断或终止的线程可能无限地阻止系统范围内的向前。可以通过基于互斥锁、信号量等同步机制实现,但是阻塞/中断的线程可能无限阻止系统向前推进。

队列结构(先进先出)

  1. 链表 首尾指针移动实现 | 动态分配
    √:简单,动态扩容,无边界
    ×:频繁分配空间,降低进程的性能
  2. 循环数组 索引取余实现| 预先分配
    √:不会频繁分配空间
    ×:固定队列数量,有边界,可能造成空间浪费
  3. 混合式(块状链表)
    结合链表和数组的优点,尽量避免两者的缺点
    请添加图片描述

动态分配&预先分配的特点

动态:多个线程如果频繁从一个共享堆上申请资源分配空间,性能会降低
预先:效率高,但空间大小固定,if当前线程需要大空间则要重新分配空间,if此队列长期只利用了少量元素,则空间资源浪费

为什么需要无锁队列 (影响队列性能因素)

影响队列性能的因素:频繁在堆上分分配空间,可以通过使用内存解决

  1. 多线程环境下锁的开销
    线程切换,上下文切换,cache损坏
    时间浪费在保护队列时争夺上面,而不是用在执行任务
  2. 不能使用基于锁的情况
    信号处理程序(打断运行)、硬实时系统(执行时间有限)等

2. 无锁队列分类

生产者-消费者队列

如果只有1个生产者和1个消费者线程,可以使用SPSC队列而不
是更一般的MPMC队列,它将明显更快。

  • MPMC (多生产者多消费者) 最常用的通用型队列;
    特点:由于多个生产者和消费者可能会同时访问队列,因此需要更复杂的并发控制机制来保证线程安全。通常会使用 CAS 等原子操作来处理多个线程对队列的并发访问,避免数据竞争和不一致的问题。
  • SPMC (单生产者多消费者) 相对少见;
    特点:需要处理多个消费者之间的并发问题,保证多个消费者能够安全地从队列中取出元素。同时,要确保生产者在添加元素时不会受到消费者的干扰。
  • MPSC (多生产者单消费者) 常见于任务分发场景;
    特点:实现时需要重点处理多个生产者之间的并发问题,确保多个生产者能够安全地向队列中添加元素。而对于消费者线程,由于是唯一的,处理相对简单。
  • SPSC (单生产者单消费者) 效率最高但应用场景较少;
    特点:不存在多个生产者或消费者之间的竞争,所以可以使用比较简单高效的算法来实现。

怎么衡量多性能环境队列的性能高低

不是单纯比较单位时间插入元素的个数, 单位时间内取出元素的个数
核心是 单位时间内处理任务的个数

无锁队列是不是比有锁队列性能高?

设计队列的原则: 1.任务的耗时 2.生产者和消费者数量
如果是从 操作队列的 角度出发, 无锁队列性能高
如果是从 队列所属系统 角度出发, 不一定 需要看任务耗时,生产者和消费者数量


3. 开源项目分析

locked_queue (TrinityCore)

  • 队列为空时不阻塞消费者线程,适合任务耗时的情况.
    不区分生产者和消费者数量
#ifndef LOCKEDQUEUE_H
#define LOCKEDQUEUE_H
#include <deque>
#include <mutex>

template <class T, typename StorageType =
std::deque<T> >
class LockedQueue{
   
   
   //! Lock access to the queue.
   std::mutex _lock;
  //! Storage backing the queue.
   StorageType _queue;
  //! Cancellation flag.
   volatile bool _canceled;

public:
   //! Create a LockedQueue.
   LockedQueue()
      : _canceled(false)  {
   
   
  }

   //! Destroy a LockedQueue.
   virtual ~LockedQueue()   {
   
   
  }

   //! Adds an item to the queue.
   void add(const T& item){
   
   
       lock();

        _queue.push_back(item);

       unlock();
   }

   //! Adds items back to front of the queue
   template<class Iterator>
   void readd(Iterator begin, Iterator end)
  {
   
   
       std::lock_guard<std::mutex> lock(_lock);
        _queue.insert(_queue.begin(), begin,
end);
   }

    //! Gets the next result in the queue, if any.
   bool next(T& result){
   
   
       std::lock_guard<std::mutex> lock(_lock);

       if (_queue.empty())
           return false;

       result = _queue.front();
        _queue.pop_front();

       return true;
  }

   template<class Checker>
   bool next(T& result, Checker& check)   {
   
   
       std::lock_guard<std
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值