Sid*_*cts 8 c++ multithreading atomic lock-free c++11
在试图理解如何处理无锁代码的过程中,我试图编写单个消费者/单生产者锁定免费队列.和往常一样,我检查了论文,文章和代码,特别是考虑到这是一个有点微妙的主题.
所以,我偶然发现了Folly库中这个数据结构的实现,可以在这里找到:https: //github.com/facebook/folly/blob/master/folly/ProducerConsumerQueue.h
正如我看到的每个无锁队列,这个似乎使用循环缓冲区,所以我们有两个std::atomic<unsigned int>变量:readIndex_和writeIndex_.该readIndex_指示下一个索引,我们将读取,并writeIndex_在其中我们将写入下一个.看起来很简单.
因此,初看起来看起来很干净,很简单,但我发现有一件事很麻烦.事实上,一些功能,如isEmpty(),isFull()或guessSize()使用std::memory_order_consume检索索引的价值.
公平地说,我真的不知道他们的目的是什么.不要误解我的意思,我知道std::memory_order_consume在经典的依赖项中使用了一个原子指针,但是在这里,我们似乎没有任何依赖!我们只得到索引,无符号整数,我们不创建依赖项.在这种情况下,对我来说,a std::memory_order_relaxed是等价的.
但是,我不相信自己比设计此代码的人更了解内存排序,因此我在这里问这个问题.有什么我错过或误解了吗?
我提前感谢你的答案!
几个月前我也有同样的想法,所以我在 10 月份提交了这个拉取请求std::memory_order_consume,建议他们将负载更改为std::memory_order_relaxed,因为消耗根本没有意义,因为没有可以使用从一个线程到另一个线程的依赖项这些功能。它最终引发了一些讨论,揭示了isEmpty()、isFull()、 和的可能用sizeGuess例如下:
//Consumer
while( queue.isEmpty() ) {} // spin until producer writes
use_queue(); // At this point, the writes from producer _should_ be visible
Run Code Online (Sandbox Code Playgroud)
这就是为什么他们解释说这std::memory_order_relaxed不合适但std::memory_order_consume会合适。然而,这只是正确的,因为在我所知道的所有编译器上都std::memory_order_consume被提升为。std::memory_order_acquire因此,虽然std::memory_order_consume看起来可能提供了正确的同步,但将其保留在代码中并假设它将保持正确是相当误导的,特别是如果std::memory_order_consume要按预期实现的话。上述用例在较弱的架构上将无法工作,因为不会生成适当的同步。
他们真正需要的是让这些负载std::memory_order_acquire按预期工作,这就是我几天前提交另一个拉取请求的原因。或者,他们可以将获取负载从循环中取出并在最后使用栅栏:
//Consumer
while( queue.isEmpty() ) {} // spin until producer writes using relaxed loads
std::atomic_thread_fence(std::memory_order_acquire);
use_queue(); // At this point, the writes from producer _should_ be visible
Run Code Online (Sandbox Code Playgroud)
无论哪种方式,std::memory_order_consume这里都被错误地使用。
| 归档时间: |
|
| 查看次数: |
680 次 |
| 最近记录: |