【问题标题】:C++ release sequence detailsC++ 发布序列详细信息
【发布时间】:2021-03-24 02:00:15
【问题描述】:

我了解到释放序列的目的是使一个线程中的写入操作与其他线程中的许多读取操作同步。

但是看了下面的两个参考资料后,我仍然无法理解一些细节。

cppreference

  1. 由执行 A 的同一线程执行的写入 (C++20 前)
  2. 任何线程对 M 进行的原子读-修改-写操作

1.Dose "Writes executed by the same thread" 意思如下?

void foo()
{
    count.store(release)
    ....
    count.store(release)
}

为什么要强调写操作在同一个线程中?为什么只强调写入(而非读取)操作?

2.其他线程中的操作是否必须像 atomic::fetch_sub 一样 RMW ?能不能像 atomic::load 这样的普通读操作?

【问题讨论】:

  • 您要确保queue_data 的更改在新count 之前(或至少同时)对其他线程可见。这就是为什么在线程中排序很重要。

标签: c++ multithreading c++11 atomic


【解决方案1】:

释放序列是原子对象修改顺序的最大连续子序列。它以释放操作 A 为首,后跟任意数量的:

  • 由执行A
  • 的同一线程执行的原子操作
  • 原子读取-修改-写入操作。

对于 A 之后的操作,使用哪个内存顺序无关紧要 - 他们甚至可以使用 memory_order_relaxed。如果线程正在使用获取语义读取属于释放序列一部分的值,则此读取将与引导序列的释放操作同步。

请注意,同一对象上可以同时存在多个释放序列。假设在某个原子对象 A 上有两个 release-CAS 操作。由于两者都使用发布语义,它们都充当自己发布序列的头部。由于 CAS 是原子性的读-修改-写操作,因此第二个 CAS 也是以第一个 CAS 为首的释放序列的一部分。因此,返回第二个 CAS 存储的值的 A 上的获取加载实际上将与 两个 释放 CAS 操作同步。

回答您的问题 - 加载操作绝不是修改顺序的一部分(因为它们本质上不会修改对象),因此不是发布顺序的一部分。

1.Dose "Writes executed by the same thread" 意思如下?

void foo()
{
   count.store(release) // A
   ....
   count.store(release) // B
}

为什么要强调在同一个线程中的写操作?为什么只强调写(而不是读)操作?

是的,这意味着类似于您的示例,即使第二个商店(我用“B”注释)甚至可以放松。 A 将是释放序列的头部,并且观察到由 B 写入的值的某个线程的任何负载都将与 A 同步。如前所述,加载操作不是修改顺序的一部分,因此也不是发布顺序的一部分。

其他线程中的操作是否必须像 atomic::fetch_sub 这样 RMW ?能不能像 atomic::load 这样的普通读操作?

是的,任何读-修改-写操作都可以是释放序列的一部分,无论使用哪个内存顺序或哪个线程执行它。同样,加载操作与发布顺序无关。

在您的示例中,发布序列确保每个 acquire-fetch-sub 与初始发布存储同步,即使 fetch-sub 观察到的值可能是由某个先前的 fetch-sub 而不是 release-写入的store 是发布序列的头部,从而确保您在 populate queue_dataprocess queue_data 之间具有必要的发生前关系。

【讨论】:

    猜你喜欢
    • 2020-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-11
    • 2013-06-28
    • 1970-01-01
    相关资源
    最近更新 更多