文章目录
一、无锁队列实现
1. 什么是无锁队列?
使用队列的数据结构,同时用无锁的技术来锁定这些队列的操作,实现的队列叫做无锁队列。
无锁原语(lock free | wait free | blocking)
无锁 :不能使用互斥锁,只能基于原子操作,内存屏障等实现。
- lock free(无锁) : 至少保证一个线程向前移动
系统作为一个整体无论如何都向前移动,但不能保证每个线程的前进进度(可能出现一个线程移动,其他线程饿死的状态)。通常使用compare_exchange(CAS)原语实现。允许存在循环,但不能使用类似compare_exchange实现的自旋锁(可能造成死锁) - wait free(无等待) :保证所有线程向前移动
保证每个线程都能在有限步骤内向前移动,不受其他线程争用或阻塞的影响。使用exchange、fetch_add等原子操作原语实现。不允许包含可能被其他线程影响的循环。 - blocking(阻塞) :不保证所有线程向前移动
整个系统可能不会取得任何进展,阻塞、中断或终止的线程可能无限地阻止系统范围内的向前。可以通过基于互斥锁、信号量等同步机制实现,但是阻塞/中断的线程可能无限阻止系统向前推进。
队列结构(先进先出)
- 链表 首尾指针移动实现 | 动态分配
√:简单,动态扩容,无边界
×:频繁分配空间,降低进程的性能 - 循环数组 索引取余实现| 预先分配
√:不会频繁分配空间
×:固定队列数量,有边界,可能造成空间浪费 - 混合式(块状链表)
结合链表和数组的优点,尽量避免两者的缺点

动态分配&预先分配的特点:
动态:多个线程如果频繁从一个共享堆上申请资源分配空间,性能会降低
预先:效率高,但空间大小固定,if当前线程需要大空间则要重新分配空间,if此队列长期只利用了少量元素,则空间资源浪费
为什么需要无锁队列 (影响队列性能因素)
影响队列性能的因素:频繁在堆上分分配空间,可以通过使用内存解决
- 多线程环境下锁的开销
线程切换,上下文切换,cache损坏
时间浪费在保护队列时争夺上面,而不是用在执行任务 - 不能使用基于锁的情况
信号处理程序(打断运行)、硬实时系统(执行时间有限)等
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

:无锁消息队列&spm=1001.2101.3001.5002&articleId=149798057&d=1&t=3&u=97072749bc364428b2d2421279ac0b69)
1791

被折叠的 条评论
为什么被折叠?



