C++中的无锁数据结构=只使用原子和内存排序
Lock-free data structures in C++ = just use atomics and memory-ordering?
我曾经看到过"无锁数据结构"这个词,并认为"ooooo,这一定很复杂"。然而,我一直在读《C++并发在行动》,它似乎写了一个无锁的数据结构——你所做的就是停止使用互斥/锁,并用原子代码替换它们(以及可能的内存排序障碍)。
所以我的问题是——我是不是错过了什么?由于C++11,它真的那么简单吗?编写无锁数据结构只是用原子操作替换锁的一种情况吗?
哦,但真的很复杂。
如果你看不出互斥和原子访问之间的区别,那么你看待并行处理的方式就有问题,而且你编写的代码很快就会出现问题。
很可能它的运行速度会比同等的阻塞版本慢,如果你(或者你的同事)真的很不幸,它会偶尔喷出不一致的数据,并随机崩溃。
更有可能的是,它会将实时约束传播到应用程序的大部分,迫使你的同事浪费大量时间来处理他们和他们的软件本来可以很愉快地生活的任意需求,并求助于各种迷信良好实践来混淆他们的代码以提交。
哦,好吧,只要模板人和免费等待的人有他们的小乐趣。。。
并行处理,无论是阻塞处理还是所谓的无等待处理,本质上都是资源消耗、复杂且成本高昂的。设计一个从非琐碎的并行处理中获得真正优势的软件体系结构是专家的工作。
相反,一个好的软件设计应该将并行性限制在最低限度,让大多数程序员可以自由地实现线性、顺序代码。
至于C++,我发现把一根绳子、一根线和一台咖啡机用同一种语法黏糊糊地包装起来的整个哲学是一个灾难性的设计选择。
C++允许你从任何东西中创建一个多处理器同步对象,就像你只分配一个字符串一样,这类似于在同一个展示柜中把突击步枪放在水枪旁边。
毫无疑问,很多人都是靠推销突击步枪和水枪没有太大区别的想法来谋生的。但是,他们仍然是。
需要考虑的两件事:
-
当使用C++11原子时,只有单个操作是原子操作。但通常情况下,当您想要使用互斥来保护更大的代码区域时。
-
如果将std::atomic与编译器无法在机器代码中转换为原子操作的类型一起使用,则编译器将不得不为该操作插入互斥对象。
总的来说,您可能希望坚持使用互斥,只在性能关键部分使用无锁代码,或者如果您正在实现自己的结构以用于同步。
您缺少了一些东西。虽然无锁数据结构确实使用了您提到的原语,但简单地调用它们的存在并不能为您提供无锁队列。
由于C++,无锁代码并不简单,如果没有C++,操作系统通常会为C/Assembly中的内存排序和围栏提供类似的东西。
C++提供了更好的&更易于使用的接口(当然也更标准化,这样你就可以在多个操作系统中使用,多个具有相同接口的机器结构),但如果你只针对一种特定类型的操作系统/机器结构,那么用C++编程无锁代码不会比没有C++更简单。
- C++内存模型中的确切规则阻止在获取操作之前重新排序
- 危险指针的内存排序
- 内存排序在提升::无锁:队列
- 内存排序或读取-修改-写入操作,仅(读/写)内存顺序
- 针对某些特定用例的C++原子内存排序
- 无锁环形缓冲区boost示例中的内存排序
- C++具有内存排序的原子增量
- 使用向量和气泡排序的动态内存分配
- 实现快速排序的内存问题
- 从矢量中最快的擦除元素或更好地利用内存(排序基数)
- C++合并排序内存问题
- 我想在C++编写合并排序,但内存出现一些错误,无法解决
- 合并排序逻辑分配内存失败
- C++原子增量内存排序
- C++中的无锁数据结构=只使用原子和内存排序
- 对内存中的大向量进行排序的有效方法
- 合并排序中的动态内存分配
- C++内存排序一致性
- 关于C++11内存模型的奇怪结果(放松排序)
- 将C++内存排序映射到Java