目录
无锁队列
目标
传统锁的缺点
无锁队列的做法
无锁队列的优势
适用场景
临界区/非临界区
无锁队列
代传统“加锁”队列的核心技术
目标
在多线程环境下,安全的传递数据,避免锁带来的性能损耗和风险
传统锁的缺点
开销巨大:加锁和解锁是重量级操作,涉及系统调用和线程切换,耗时可能是简单指令的几百倍。
优先级反转:低优先级线程拿到锁后,高优先级线程只能干等,导致关键任务被阻塞。
死锁风险:两个线程互相等待对方释放锁,程序直接卡死。
阻塞等待:线程拿不到锁就会进入休眠,唤醒又需要开销。在量化交易这种每微秒都重要的场景,这些延迟是不可接受的。
无锁队列的做法
原子操作+精细的内存管理
让多线程能够几乎并行地安全访问
核心原理
使用原子操作:依赖CPU提供的“比较并交换”(
CAS,即Compare-And-Swap)指令。这是一个不可中断的原子操作:先比较内存中的值是否和我预期的一致,如果是,就把它更新为新值;如果不是,就重试。这为无锁编程提供了底层基础。设计无锁的算法:基于CAS设计算法(它保证的是一条CPU指令(比如“比较并交换”)是不可分割的)。安全的连接新的节点,或移动队列的“头尾”指针,保证在任何时刻,队列都处于一致状态,不会因线程交错而损坏。
总结:无锁队列的主要原理是对硬件进行原子操作,软件层面就是去使用它。
软件算法(无锁队列的实现逻辑) -> 调用原子库(std::atomic) -> 编译为CAS等CPU原子指令 -> 硬件执行,保证操作的原子性。
无锁队列的优势
极致低延迟:无锁队列用轻量的CPU指令代替了重量级的操作系统锁,延迟能从微秒级骤降至纳秒级,这在高频交易中优势巨大。
无死锁:因为根本上没使用锁,死锁风险自然不存在。
高鲁棒性:即便某个线程被系统暂停,也不会阻塞其他线程,系统整体稳定性更高。
适用场景
他的实现非常复杂,使用场景十分有限,只适合生产者、消费者这种简单的数据传递场景(数据传递量小的场景),对于复杂的操作(临界区很大,或者是操作时间很长),还是要用锁去处理。
无锁队列并不是用来替代所有锁,而在关键的数据路径上面,让数据的传递更高效。
所以无锁队列,它是会把一些数据传递的场景,去将从毫秒级优化到微秒级或者是纳秒级。
临界区/非临界区
临界区:一段访问共享资源(比如全局变量、队列、文件)的代码。这段代码不能被多个线程同时执行,否则程序就会出错。
锁的操作就是对这个临界区,进行这个加锁和解锁的操作,因为临界区内的资源是共享的资源,如果多个线程同时访问这个资源的话,就会有数据竞争的现象,所以临界区的资源必须是原子性操作
临界区的资源必须是原子性操作