当前位置: 首页 > news >正文

C++ 无锁队列:原理与实现

引言

在多线程编程中,队列是一种常用的数据结构。传统的队列在多线程环境下访问时,通常需要使用锁机制来保证数据的一致性和线程安全。然而,锁的使用会带来性能开销,尤其是在高并发场景下,频繁的加锁和解锁操作可能成为性能瓶颈。无锁队列作为一种高效的替代方案,通过使用原子操作和巧妙的内存管理,避免了锁的使用,从而提升了多线程环境下的性能。本文将深入探讨 C++ 中无锁队列的实现原理,并给出详细的代码示例。

无锁队列的原理

无锁队列基于 CAS(Compare - And - Swap)操作来实现。CAS 是一种原子操作,它将内存位置的内容与给定值进行比较,如果相等,则将该内存位置的内容修改为新的值。在无锁队列中,入队和出队操作通过 CAS 操作来更新队列的头部和尾部指针,从而避免了锁的使用。

假设有一个简单的节点结构用于构成队列:

 

template<typename T>

struct Node {

T data;

std::atomic<Node*> next;

Node(const T& value) : data(value), next(nullptr) {}

};

这里的next指针被声明为std::atomic<Node*>类型,以确保对其进行的操作是原子的,避免多线程环境下的竞态条件。

入队操作

入队操作的基本步骤如下:

  1. 创建一个新节点,将数据放入新节点。
  1. 找到队列的尾部节点。
  1. 使用 CAS 操作将新节点链接到尾部节点的next指针上。
  1. 如果 CAS 操作失败,说明在找到尾部节点后,队列发生了变化(其他线程进行了入队或出队操作),需要重新执行步骤 2 和 3。
  1. 成功链接新节点后,使用 CAS 操作更新队列的尾部指针指向新节点。

出队操作

出队操作的基本步骤如下:

  1. 检查队列是否为空。
  1. 找到队列的头部节点。
  1. 获取头部节点的下一个节点。
  1. 使用 CAS 操作将队列的头部指针更新为下一个节点。
  1. 如果 CAS 操作失败,说明在获取头部节点后,队列发生了变化,需要重新执行步骤 2 到 4。
  1. 释放旧的头部节点。

无锁队列的实现代码

 

template<typename T>

class LockFreeQueue {

private:

std::atomic<Node<T>*> head;

std::atomic<Node<T>*> tail;

public:

LockFreeQueue() {

Node<T>* sentinel = new Node<T>(T());

head.store(sentinel);

tail.store(sentinel);

}

~LockFreeQueue() {

while (Node<T>* node = head.load()) {

head.store(node->next.load());

delete node;

}

}

void enqueue(const T& value) {

Node<T>* newNode = new Node<T>(value);

while (true) {

Node<T>* tailNode = tail.load();

Node<T>* nextNode = tailNode->next.load();

if (tailNode == tail.load()) {

if (!nextNode) {

if (tailNode->next.compare_exchange_weak(nextNode, newNode)) {

tail.compare_exchange_weak(tailNode, newNode);

return;

}

}

else {

tail.compare_exchange_weak(tailNode, nextNode);

}

}

}

}

bool dequeue(T& result) {

while (true) {

Node<T>* headNode = head.load();

Node<T>* tailNode = tail.load();

Node<T>* nextNode = headNode->next.load();

if (headNode == head.load()) {

if (headNode == tailNode) {

if (!nextNode) {

return false;

}

tail.compare_exchange_weak(tailNode, nextNode);

}

else {

result = nextNode->data;

if (head.compare_exchange_weak(headNode, nextNode)) {

delete headNode;

return true;

}

}

}

}

}

};

代码解析

  1. 构造函数:在构造函数中,创建一个哨兵节点(sentinel node),并将head和tail指针都指向该节点。哨兵节点用于简化边界条件的处理,避免在队列为空时进行额外的判断。
  1. 析构函数:析构函数遍历队列,释放所有节点的内存。它通过不断地加载head指针,并将其更新为下一个节点,然后删除当前节点,直到head指针为空。
  1. 入队函数enqueue
    • 首先创建一个新节点。
    • 进入一个无限循环,在循环中不断尝试找到队列的尾部节点。
    • 检查当前找到的尾部节点的next指针是否为空。如果为空,尝试使用compare_exchange_weak(一种 CAS 操作)将新节点链接到尾部节点的next指针上。如果链接成功,再尝试更新tail指针指向新节点。
    • 如果当前找到的尾部节点的next指针不为空,说明有其他线程在当前线程找到尾部节点后进行了入队操作,此时需要更新tail指针,使其指向next指针所指向的节点,然后重新尝试入队。
  1. 出队函数dequeue
    • 进入一个无限循环,在循环中不断尝试找到队列的头部节点。
    • 检查队列是否为空(即head和tail指针是否指向同一个节点且该节点的next指针为空)。如果为空,返回false表示出队失败。
    • 如果队列不为空,获取头部节点的下一个节点的数据。
    • 使用compare_exchange_weak尝试更新head指针,使其指向头部节点的下一个节点。如果更新成功,说明出队操作成功,删除旧的头部节点并返回true。
    • 如果更新失败,说明有其他线程在当前线程获取头部节点后进行了入队或出队操作,此时需要重新尝试出队。

无锁队列使用示例

下面是一个简单的多线程环境中使用无锁队列的示例代码,展示了如何创建无锁队列对象,以及多个线程同时进行入队和出队操作:

 

#include <iostream>

#include <thread>

#include <vector>

#include "LockFreeQueue.h" // 假设无锁队列实现代码在该头文件中

void producer(LockFreeQueue<int>& queue, int value) {

queue.enqueue(value);

std::cout << "Produced: " << value << std::endl;

}

void consumer(LockFreeQueue<int>& queue) {

int result;

if (queue.dequeue(result)) {

std::cout << "Consumed: " << result << std::endl;

}

else {

std::cout << "Queue is empty" << std::endl;

}

}

int main() {

LockFreeQueue<int> queue;

std::vector<std::thread> threads;

// 创建多个生产者线程

for (int i = 0; i < 3; ++i) {

threads.emplace_back(producer, std::ref(queue), i * 10);

}

// 创建多个消费者线程

for (int i = 0; i < 3; ++i) {

threads.emplace_back(consumer, std::ref(queue));

}

// 等待所有线程完成

for (auto& thread : threads) {

thread.join();

}

return 0;

}

在上述示例中,定义了producer和consumer函数,分别用于向无锁队列中入队和出队数据。在main函数中,创建了一个LockFreeQueue<int>对象,并启动了多个生产者线程和消费者线程。生产者线程向队列中插入不同的值,消费者线程从队列中取出数据并打印。通过这种方式,可以直观地看到无锁队列在多线程环境下的工作情况。

性能优势与适用场景

无锁队列在高并发场景下具有显著的性能优势,因为它避免了锁带来的线程阻塞和上下文切换开销。在多线程频繁访问队列的情况下,无锁队列能够提供更高的吞吐量和更低的延迟。然而,无锁队列的实现相对复杂,代码可读性和可维护性较差。因此,在低并发场景下,或者对代码复杂度有严格要求的场景中,传统的带锁队列可能是更合适的选择。

总结

无锁队列是一种在多线程编程中非常有用的数据结构,通过巧妙地使用原子操作和内存管理,避免了锁的使用,从而提升了性能。本文详细介绍了无锁队列的原理,并给出了完整的 C++ 实现代码及使用示例。希望通过本文的介绍,读者能够深入理解无锁队列的工作机制,并在实际项目中灵活运用这一技术。

相关文章:

C++ 无锁队列:原理与实现

引言 在多线程编程中&#xff0c;队列是一种常用的数据结构。传统的队列在多线程环境下访问时&#xff0c;通常需要使用锁机制来保证数据的一致性和线程安全。然而&#xff0c;锁的使用会带来性能开销&#xff0c;尤其是在高并发场景下&#xff0c;频繁的加锁和解锁操作可能成…...

web的分离不分离:前后端分离与不分离全面分析

让我们一起走向未来 &#x1f393;作者简介&#xff1a;全栈领域优质创作者 &#x1f310;个人主页&#xff1a;百锦再新空间代码工作室 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[1504566…...

MobileSal:极其高效的RGB-D显著性物体检测模型

摘要 问题一&#xff1a;什么叫做MobileSal&#xff1f; MobileSal 是指一种用于移动设备上的显著性检测&#xff08;Saliency Detection&#xff09;方法&#xff0c;通常是针对在资源受限的环境&#xff08;如智能手机&#xff09;上运行的视觉模型。 问题二&#xff1a;什…...

【个人总结】1. 开发基础 工作三年的嵌入式常见知识点梳理及开发技术要点(欢迎指正、补充)

【个人总结】1. 开发基础 工作三年的嵌入式常见知识点梳理及开发技术要点&#xff08;欢迎指正、补充&#xff09; 工作快三年以来 分别进行了嵌入式MCU及外设开发、RTOS、传感器、文件系统及USB、Linux、GUI、通讯协议、毫米波雷达、少量的DSP和物联网开发。 特此总结&#x…...

硬核技术组合!用 DeepSeek R1、Ollama、Docker、RAGFlow 打造专属本地知识库

文章目录 一、引言二、安装Ollama部署DeepSeekR1三、安装Docker四、安装使用RAGFlow4.1 系统架构4.2 部署流程4.3 使用RAGFlow4.4 在RAGFlow中新增模型4.5 创建知识库4.6 创建私人助理使用RGA 一、引言 本地部署DeepSeek R1 Ollama RAGFlow构建个人知识库&#xff0c;通过将…...

MySQL官网驱动下载(jar包驱动和ODBC驱动)【详细教程】

1.打开MySQL的官网&#xff0c;选择下载(Download) MySQL[这里是图片001]https://www.mysql.com/cn/ 2.往下划点击MySQL Community(GPL)Downloads 3.要下载MySQL的jar包的选择Connector/J 4.进入后&#xff0c;根据自己的需求选择相应的版本 5.下载完成后&#xff0c;进行解压…...

idea 2019.3常用插件

idea 2019.3常用插件 文档 idea 2019.3常用插件idea 2023.3.7常用插件 idea 2019.3常用插件 插件名称插件版本说明1AceJump3.5.9AceJump允许您快速将插入符号导航到编辑器中可见的任何位置。只需按“ctrl&#xff1b;”&#xff0c;键入一个字符&#xff0c;然后在Ace Jump…...

对CSS了解哪些?

CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是用来描述HTML文档外观和布局的语言。以下是对CSS的常见了解范围&#xff1a; 1. CSS 基础 选择器&#xff1a;如通用选择器 (*)、类型选择器、类选择器 (.class)、ID选择器 (#id)、后代选择器、伪类…...

TikTok账户安全指南:如何取消两步验证?

TikTok账户安全指南&#xff1a;如何取消两步验证&#xff1f; 在这个数字化的时代&#xff0c;保护我们的在线账户安全变得尤为重要。TikTok&#xff0c;作为全球流行的社交媒体平台&#xff0c;其账户安全更是不容忽视。两步验证作为一种增强账户安全性的措施&#xff0c;虽…...

从零到一:构建现代 React 应用的完整指南

1. create-react-app (CRA) 简介: create-react-app 是官方推荐的 React 项目脚手架工具,提供了一个开箱即用的开发环境,帮助开发者快速启动 React 应用。它会自动配置 Webpack、Babel、ESLint 等工具,让你专注于开发而不需要手动配置工具链。 特点: 零配置:CRA 自动配…...

【Python爬虫(26)】Python爬虫进阶:数据清洗与预处理的魔法秘籍

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…...

机器学习数学基础:28.卡方检验

卡方检验教程 一、引言 在统计学的广阔领域中&#xff0c;卡方检验&#xff08;Chi - Square Test&#xff09;宛如一把锐利的手术刀&#xff0c;能够精准剖析数据背后隐藏的关系与模式。它主要用于两大核心任务&#xff1a;一是深入分析两个及两个以上分类变量之间错综复杂的…...

【工具插件类教学】实现运行时2D物体交互的利器Runtime2DTransformInteractor

目录 ​编辑 1. 插件核心功能 1.1 基础变换操作 1.2 高级特性 2. 安装与配置 2.1 导入插件 2.2 配置控制器参数 2.3 为物体添加交互功能 3. 使用示例 3.1 基础操作演示 3.2 多选与批量操作 3.3 自定义光标与外观 4. 高级配置技巧 4.1 动态调整包围框控件尺寸 4.…...

回调处理器

文章目录 什么是回调处理器回调处理器的工作流程回调处理器的使用自定义链组件中的回调 内置回调处理器自定义回调处理器 在编程领域中&#xff0c;回调是一个非常重要的概念。简而言之&#xff0c;回调是一种特殊的函数或方法&#xff0c;它可以被传递给另一个函数作为参数&am…...

Redis-03高级篇中-多级缓存:

说明&#xff1a; 分布式缓存和多级缓存的视频&#xff0c;与springcloud高级篇redis的一模一样。这里就不在重复学习了&#xff0c;如果后面用到关于redis的配置&#xff0c;直接到springcloud模块安装的redis中学习即可。 多级缓存 0.学习目标 1.什么是多级缓存 传统的缓…...

Spring Boot ShardingJDBC分库分表(草稿)

ShardingJDBC分库分表 1.Maven 引用 <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.1.1</version></dependency><dependency><…...

Jenkins 环境搭建---基于 Docker

前期准备 提前安装jdk、maven、nodeJs&#xff08;如果需要的话&#xff09; 创建 jenkins 环境目录&#xff0c;用来当做挂载卷 /data/jenkins/ 一&#xff1a;拉取 Jenkins 镜像 docker pull jenkins/jenkins:lts 二&#xff1a;设置 Jenkins挂载目录 mkdir -p ~/jen…...

如何在自定义组件中使用v-model实现双向绑定

在 Vue 2 中&#xff0c;v-model 是双向数据绑定的语法糖&#xff0c;它默认将 value 作为 prop 传入组件&#xff0c;并通过监听 input 事件来更新父组件的数据。若要在自定义组件中实现 v-model 的双向绑定&#xff0c;需遵循以下步骤&#xff1a; 1. 基本实现&#xff1a;va…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_pool_t 类型

ngx_pool_t 定义在 src/core/ngx_core.h typedef struct ngx_pool_s ngx_pool_t; ngx_pool_s 定义在 src/core/ngx_palloc.h struct ngx_pool_s {ngx_pool_data_t d;size_t max;ngx_pool_t *current;ngx_chain_t *chain;ng…...

如何在 ubuntu 上使用 Clash 与 docker 开启代理拉起

如何在 ubuntu 上使用 Clash https://github.com/doreamon-design/clash/releases上面是clash 的地址 clash_2.0.24_linux_386.tar.gz 下载 386 的 如果你的电脑是inter tar -xzvf clash_2.0.24_linux_386.tar.gz 启动 ./clash 然后会在电脑上生成一个config的文件 /home/xxx/…...

模型量化实战:从零实现PyTorch训练后量化(PTQ)全流程

1. 什么是训练后量化&#xff08;PTQ&#xff09;&#xff1f; 训练后量化&#xff08;Post-Training Quantization&#xff0c;简称PTQ&#xff09;是一种常见的模型压缩技术&#xff0c;它能在不重新训练模型的情况下&#xff0c;将浮点模型转换为低精度整型模型。简单来说&a…...

JS逆向和前端加密暴力破解(小白无痛学习),黑客技术零基础入门到精通教程!

网站运行的时间轴url–>加载html–>加载js–>运行js初始化–>用户触发某个事件–调用了某段js–>明文数据–>加密函数–>加密后的 数据–>send&#xff08;给服务器发信息{XHR–SEND}&#xff09; -->接收到服务器数据–>解密函数–>刷新函数…...

Unity PSD导入器终极指南:如何快速将Photoshop文件转换为Unity游戏资源 [特殊字符]

Unity PSD导入器终极指南&#xff1a;如何快速将Photoshop文件转换为Unity游戏资源 &#x1f3ae; 【免费下载链接】UnityPsdImporter Advanced PSD importer for Unity3D 项目地址: https://gitcode.com/gh_mirrors/un/UnityPsdImporter 核心关键词&#xff1a;Unity P…...

VRM4U与LiveLinkFace:打造实时面部动画的终极解决方案

VRM4U与LiveLinkFace&#xff1a;打造实时面部动画的终极解决方案 【免费下载链接】VRM4U Runtime VRM loader for UnrealEngine5 项目地址: https://gitcode.com/gh_mirrors/vr/VRM4U VRM4U是专为Unreal Engine设计的运行时VRM加载器&#xff0c;能够将VRM虚拟角色模型…...

S32K344 Flash Driver实战:手把手教你用C40_Ip库实现任意字节写入与扇区解锁

S32K344 Flash驱动深度实战&#xff1a;突破C40_Ip库8字节对齐限制的工程解决方案 从真实案例看Flash驱动的工程挑战 去年在为某新能源车厂开发OTA升级功能时&#xff0c;我们团队遇到了一个典型的嵌入式开发困境&#xff1a;S32K344微控制器的官方Flash驱动库C40_Ip强制要求所…...

数字孪生落地指南与技术选型:从选型到交付全流程避坑实战 | 数字孪生实战训练营

⚠️ 说明&#xff1a;本文内容偏实践经验总结&#xff0c;更适合有数字孪生项目背景或正在推进相关工作的读者阅读。 在数字化转型的深水区&#xff0c;数字孪生已不再仅仅是炫酷的视觉概念&#xff0c;而是深入业务一线、赋能决策的核心工具。然而&#xff0c;从概念雏形到最…...

终极下载管理解决方案:AB Download Manager 完全指南

终极下载管理解决方案&#xff1a;AB Download Manager 完全指南 【免费下载链接】ab-download-manager A Download Manager that speeds up your downloads 项目地址: https://gitcode.com/GitHub_Trending/ab/ab-download-manager 你是否经常被杂乱无章的下载文件困扰…...

深度体验:8款AI网课总结工具使用心得,看看哪款适合你?

面对长达几小时的网课视频&#xff0c;你是否也曾因为记不全要点而焦虑&#xff1f;回看录像不仅耗时&#xff0c;还往往抓不住重点&#xff0c;导致复习效率低下。作为一名深受笔记整理困扰的学习者&#xff0c;我开始尝试使用“AI网课总结工具”。通过AI自动提取核心逻辑、生…...

3步完成MOOC课程永久保存:MoocDownloader的离线学习解决方案

3步完成MOOC课程永久保存&#xff1a;MoocDownloader的离线学习解决方案 【免费下载链接】MoocDownloader An MOOC downloader implemented by .NET. 一枚由 .NET 实现的 MOOC 下载器. 项目地址: https://gitcode.com/gh_mirrors/mo/MoocDownloader 你是否曾因网络不稳定…...

Anime4K终极指南:浏览器中实时观看4K动漫的完整解决方案

Anime4K终极指南&#xff1a;浏览器中实时观看4K动漫的完整解决方案 【免费下载链接】Anime4K A High-Quality Real Time Upscaler for Anime Video 项目地址: https://gitcode.com/gh_mirrors/an/Anime4K 想象一下这样的场景&#xff1a;你珍藏多年的老动漫&#xff0c…...