C++并发实战19:lock free编程

发布时间 - 2025-07-20 00:00:00    点击率:

大家好,又见面了,我是你们的朋友全栈君。涉及到并行/并发计算时,通常都会想到使用锁来保护共享的数据,但锁的使用也存在一些问题:

  1. 效率降低:由于临界区无法并发运行,进入临界区需要等待,锁的使用导致效率下降。多核CPU也无法充分发挥其性能。
  2. 死锁风险:在复杂的情况下,很容易造成死锁,导致并发进程或线程之间无止境地互相等待。
  3. 中断/信号处理函数中的限制:在中断或信号处理函数中不能使用锁,这给并发处理带来了困难。
  4. 影响实时性:锁会影响实时性,等待时间不确定。
  5. 优先级反转:高优先级的线程可能需要等待低优先级的线程。
  6. 线程挂起问题:如果一个线程在持有锁的情况下挂起,会影响其他等待该锁的线程。

总之,在基于锁的多线程/多进程编程中,你需要确保对竞争条件敏感的共享数据上的任何操作,都通过加锁或解锁一个互斥锁(mutex)来实现原子操作。

现有的加锁和无锁编程的种类如下:

其中,标注为红色字体的方案为Blocking synchronization(需要锁),黑色字体的为Non-blocking synchronization(无锁)。Lock-based和Lockless-based两者之间的区别仅仅是加锁粒度的不同。图中最底层的方案就是大家经常使用的mutex和semaphore等方案,代码复杂度低,但运行效率也最低。

可以使用std::atomic来实现lock free编程,但这里并不是真正的无锁,只有atomic_flag是无锁的,其他atomic内部都是有锁的,只不过粒度很小。atomic::compare_exchange_weak/strong等同于CAS(比较并交换)操作,在C++11之前该操作是平台相关的,现在atomic将其实现为成员函数。

下面是一个lock free的栈的示例:

#include 
#include 

template class lock_free_stack // 栈的底层数据结构采用单向链表实现 { private: struct node { std::shared_ptr data; // 这里采用sharedptr管理的好处在于:若栈内存放对象,pop中return栈顶对象可能拷贝异常,栈内只存储指针还可以提高性能 node* next; node(T const& data) : data(std::makeshared(data)) // 注意make_shared比直接shared_ptr构造的内存开销小 {} }; std::atomic head; // 采用原子类型管理栈顶元素,主要利用atomic::compare_exchange_weak实现lock free

public: void push(T const& data) { node* const new_node = new node(data); new_node->next = head.load(); // 每次从链表头插入 while (!head.compare_exchange_weak(new_node->next, new_node)); // 若head==new_node->next则更新head为new_node,返回true结束循环,插入成功;若head!=new_node->next表明有其它线程在此期间对head操作了,将new_node->next更新为新的head,返回false,继续进入下一次while循环。这里采用atomic::compare_exchange_weak比atomic::compare_exchange_strong快,因为compare_exchange_weak可能在元素相等的时候返回false所以适合在循环中,而atomic::compare_exchange_strong保证了比较的正确性,不适合用于循环 }

std::shared_ptrzuojiankuohaophpcnTyoujiankuohaophpcn pop()
{
    node* old_head = head.load(); // 拿住栈顶元素,但是可能后续被更新,更新发生在head!=old_head时
    while (old_head && !head.compare_exchange_weak(old_head, old_head-youjiankuohaophpcnnext)); // 这里注意首先要先判断old_head是否为nullptr防止操作空链表,然后按照compare_exchange_weak语义更新链表头结点。若head==old_head则更新head为old_head-youjiankuohaophpcnnext并返回true,结束循环,删除栈顶元素成功;若head!=old_head表明在此期间有其它线程操作了head,因此更新old_head为新的head,返回false进入下一轮循环,直至删除成功。
    return old_head ? old_head-youjiankuohaophpcndata : std::shared_ptrzuojiankuohaophpcnTyoujiankuohaophpcn(); // 这里注意空链表时返回的是一个空的shared_ptr对象
} // 这里只是lock free,由于while循环可能无限期循环不能在有限步骤内完成,故不是wait free

};

发布者:全栈程序员栈长,转载请注明出处:https://www./link/46c6a6c72edf42b1335217a9eb4b2325 原文链接:https://www./link/c8377ad2a50fb65de28b11cfc628d75c


# ai  # c++  # 区别  # 无锁  # red  # 有锁  # html  # 成员函数  #   # 线程  # 多线程  # 并发  # https  # 死锁  # 链表  # 在此  # 加锁  # 能在  # 作了  # 多核  # 来实现  # 则更  # 挂起 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)  简历在线制作网站免费版,如何创建个人简历?  Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】  标题:Vue + Vuex 项目中正确使用 JWT 进行身份认证的实践指南  Python进程池调度策略_任务分发说明【指导】  黑客入侵网站服务器的常见手法有哪些?  Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程  edge浏览器无法安装扩展 edge浏览器插件安装失败【解决方法】  JS中页面与页面之间超链接跳转中文乱码问题的解决办法  深入理解Android中的xmlns:tools属性  香港服务器如何优化才能显著提升网站加载速度?  实现点击下箭头变上箭头来回切换的两种方法【推荐】  极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?  Laravel怎么使用artisan命令缓存配置和视图  如何在七牛云存储上搭建网站并设置自定义域名?  Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】  香港服务器租用每月最低只需15元?  网站制作软件免费下载安装,有哪些免费下载的软件网站?  高端建站如何打造兼具美学与转化的品牌官网?  西安专业网站制作公司有哪些,陕西省建行官方网站?  Laravel怎么实现支付功能_Laravel集成支付宝微信支付  Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】  Laravel怎么实现模型属性的自动加密  如何制作一个表白网站视频,关于勇敢表白的小标题?  Gemini手机端怎么发图片_Gemini手机端发图方法【步骤】  HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】  Android GridView 滑动条设置一直显示状态(推荐)  html5的keygen标签为什么废弃_替代方案说明【解答】  HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】  Laravel怎么实现API接口鉴权_Laravel Sanctum令牌生成与请求验证【教程】  悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】  企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?  Laravel的.env文件有什么用_Laravel环境变量配置与管理详解  如何在HTML表单中获取用户输入并用JavaScript动态控制复利计算循环  Laravel怎么使用Markdown渲染文档_Laravel将Markdown内容转HTML页面展示【实战】  详解Android——蓝牙技术 带你实现终端间数据传输  如何在阿里云部署织梦网站?  Midjourney怎样加参数调细节_Midjourney参数调整技巧【指南】  如何快速搭建高效服务器建站系统?  Laravel如何生成和使用数据填充?(Seeder和Factory示例)  java获取注册ip实例  油猴 教程,油猴搜脚本为什么会网页无法显示?  Laravel如何发送系统通知?(Notification渠道示例)  如何在Windows 2008云服务器安全搭建网站?  Android Socket接口实现即时通讯实例代码  详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)  Laravel项目如何进行性能优化_Laravel应用性能分析与优化技巧大全  Laravel怎么使用Intervention Image库处理图片上传和缩放  如何在Windows服务器上快速搭建网站?  如何在阿里云虚拟服务器快速搭建网站?