一、atomic概念原子操作是指不可中断的操作即一个操作的执行过程中不会被其他线程打断要么全部执行完毕要么根本不执行。举个简单例子多线程环境下对一个变量i执行i操作看似是一个单一操作实则包含三个步骤读取i的值、将i的值加1、将结果写回i。如果没有原子性保证多个线程同时执行时会出现线程安全问题比如两个线程同时读取i1都加1后写回最终i2而非预期的3。atomic的核心作用解决多线程环境下的数据竞争问题避免线程安全隐患替代 synchronized 锁实现更高效的并发控制atomic基于CAS机制无锁操作性能优于锁保证操作的原子性、可见性和有序性部分场景下。二、atomic实现无锁队列完整代码如下#includeiostream #includeatomic using namespace std; // 队列节点结构 templatetypename T struct Node { T data; // 节点存储的数据 Node* next; // 指向后一个节点的指针 // 节点构造函数初始化数据和next指针 Node(T val) : data(val), next(nullptr) {} }; // 无锁队列类模板实现支持任意数据类型 templatetypename T class LockFreeQueue { private: // 原子指针head队列头、tail队列尾 atomicNodeT* head; atomicNodeT* tail; public: // 构造函数初始化队列带头节点避免空队列判断复杂 LockFreeQueue() { // 创建哑节点dummy node作为队列的哨兵节点 NodeT* dummy new NodeT(T{}); // 原子存储将head和tail都指向哑节点 head.store(dummy); tail.store(dummy); } // 入队操作将数据val加入队列尾部 void enqueue(T val) { // 1. 创建新节点存储待入队数据 NodeT* newNode new NodeT(val); // 2. 原子交换将tail指向新节点并返回原来的tailoldTail // memory_order_acq_rel保证原子操作的内存可见性和有序性 NodeT* oldTail tail.exchange(newNode, memory_order_acq_rel); // 3. 将原来的尾节点的next指向新节点完成链表链接 oldTail-next newNode; } // 出队操作将队列头部数据取出存入val返回是否出队成功 bool dequeue(T val) { // 1. 原子加载head指针获取当前队列头 NodeT* oldHead head.load(memory_order_acquire); // 2. 获取head的下一个节点真正存储数据的节点哑节点不存数据 NodeT* next_node oldHead-next; // 3. 判断队列是否为空next_node为nullptr说明只有哑节点 if (next_node nullptr) { return false; // 队列为空出队失败 } // 4. 取出next_node的数据真正要出队的数据 val next_node-data; // 5. 原子存储将head指向next_node完成头节点更新 head.store(next_node, memory_order_release); // 6. 释放原来的头节点哑节点避免内存泄漏 delete oldHead; return true; // 出队成功 } // 析构函数释放队列所有节点避免内存泄漏 ~LockFreeQueue() { NodeT* cur head.load(); while (cur ! nullptr) { NodeT* tmp cur; cur cur-next; delete tmp; } } }; // 测试代码 int main() { // 实例化一个存储int类型的无锁队列 LockFreeQueueint q; // 入队两个元素 q.enqueue(10); q.enqueue(20); int val; // 出队并打印结果 if (q.dequeue(val)) { cout Dequeued: val endl; } if (q.dequeue(val)) { cout Dequeued: val endl; } return 0; }运行输出Dequeued: 10 Dequeued: 20代码拆解1 节点结构Node队列采用单向链表实现每个Node节点包含两个核心成员data存储具体的数据支持任意类型通过模板T实现next指向后一个节点的指针用于维护链表的连续性。构造函数Node(T val)用于初始化节点将数据val存入data并将next指针置为nullptr初始状态下无后续节点。2 无锁队列类LockFreeQueue该类是核心通过两个原子指针atomicNodeT*实现无锁的入队和出队操作避免了传统锁机制的开销。2.1 私有成员原子指针head和tail核心设计用atomic包装Node指针保证对指针的读写操作是原子性的——即多个线程同时读写head/tail时不会出现“读取一半被打断”“写入冲突”等问题。为什么用原子指针普通指针的读写的非原子的比如线程A读取head指针线程B同时修改head指针可能导致线程A读取到一个“无效指针”只读取了指针的一半字节引发程序崩溃。而atomicNode*会通过CPU原子指令保证指针的读写操作完整、不可中断。2.2 构造函数初始化哑节点队列初始化时创建一个哑节点dummy node并将head和tail都指向这个哑节点。设计哑节点的核心目的是避免空队列的复杂判断——如果没有哑节点队列空时head和tail都为nullptr入队、出队时需要额外判断nullptr容易引发并发问题有了哑节点队列始终至少有一个节点哑节点head和tail永远不会为nullptr简化了逻辑。2.3 入队操作enqueue入队操作是无锁队列的核心全程无锁通过原子操作保证线程安全步骤拆解创建新节点NodeT* newNode new NodeT(val);将待入队的数据val存入新节点原子交换tail指针tail.exchange(newNode, memory_order_acq_rel);exchange是atomic的核心成员函数将tail的值当前尾节点指针替换为newNode并返回替换前的tail值oldTailmemory_order_acq_rel内存序保证该原子操作的“获取-释放”语义——确保所有线程能看到该操作之前的内存写入同时该操作的写入能被后续线程看到避免指令重排导致的并发问题。链接新节点oldTail-next newNode;将原来的尾节点oldTail的next指针指向新节点完成链表的链接此时新节点成为新的尾节点。为什么入队不需要锁因为tail.exchange是原子操作多个线程同时入队时只有一个线程能成功将tail替换为自己的新节点其他线程会拿到更新后的tail前一个线程的新节点并将自己的新节点链接到该tail之后不会出现节点链接混乱。2.4 出队操作dequeue出队操作同样基于原子操作步骤拆解原子加载head指针NodeT* oldHead head.load(memory_order_acquire);获取当前队列头哑节点获取有效数据节点NodeT* next_node oldHead-next;next_node才是真正存储数据的节点哑节点的next指向第一个有效节点判断队列是否为空如果next_node为nullptr说明队列中只有哑节点无有效数据返回false出队失败取出数据val next_node-data;将有效节点的数据存入val通过引用传出原子更新head指针head.store(next_node, memory_order_release);将head指向next_node新的哑节点释放旧哑节点delete oldHead;避免内存泄漏返回true出队成功。注意memory_order_acquire加载时和memory_order_release存储时的配合保证了出队操作的内存可见性——线程A出队后线程B能立即看到head的更新不会读取到旧的head指针。3 测试代码main函数测试逻辑简单易懂实例化一个存储int类型的无锁队列LockFreeQueueint q调用enqueue入队两个整数10、20调用dequeue出队判断出队是否成功若成功则打印出队数据运行后输出两个出队数据验证队列功能正常。