#include <atomic>
#include <thread>
std::vector<int> queue_data;
std::atomic<int> count;
void populate_queue()
{
unsigned const number_of_items=20;
queue_data.clear();
for(unsigned i=0;i<number_of_items;++i)
{
queue_data.push_back(i);
}
count.store(number_of_items,std::memory_order_release);
}
void consume_queue_items()
{
while(true)
{
int item_index;
if((item_index=count.fetch_sub(1,std::memory_order_acquire))<=0)
{
wait_for_more_items();
continue;
}
process(queue_data[item_index-1]);
}
}
int main() {
std::thread a(populate_queue);
std::thread b(consume_queue_items);
std::thread c(consume_queue_items);
a.join();
b.join();
c.join();
}
这是《C++ 并发实践》一书第 5.3.4 节中的代码片段。
作者这么说
没有释放顺序规则或内存_顺序_释放 fetch_sub 操作,没有什么需要 存储到queue_data对第二个消费者是可见的,并且您 将会发生数据竞赛。
我不明白为什么我们需要在 fetch_sub 上释放来同步存储的数据(当然我们需要同步计数以避免重复处理)但是为什么第二个消费者不与
populate_queue
线程的释放同步?
我不明白为什么我们需要在fetch_sub上发布
你不知道。请注意,代码使用了获取并表示这是可以的。
为什么第二个消费者不与 populate_queue 线程的释放同步?
确实如此。
注意书中引用的内容(强调我的):
如果 fetch_sub 操作没有释放顺序规则或内存_顺序_释放,
发布顺序规则是怎么说的,引用自标准:
以原子对象 M 上的释放操作 A 为首的释放序列是 M 的修改顺序中副作用的最大连续子序列,其中第一个操作是 A,后续的每个操作都是原子读取-修改-写操作
因此,您拥有以下形式的发布序列:生产者 -> 消费者 1 -> 消费者 2。重点是,消费者 1 覆盖
count
的值不会破坏发布顺序,因此消费者 2 仍然与生产者同步。