【问题标题】:Ordering of read/write operations in a C++ queueC++ 队列中读/写操作的排序
【发布时间】:2016-04-13 19:34:47
【问题描述】:

假设我们有一个具有以下实现的 SyncQueue 类:

class SyncQueue {
    std::mutex mtx;
    std::queue<std::shared_ptr<ComplexType> > m_q;
public:
    void push(const std::shared_ptr<ComplexType> & ptr) {
        std::lock_guard<std::mutex> lck(mtx);
        m_q.push(ptr);
    }
    std::shared_ptr<ComplexType> pop() {
        std::lock_guard<std::mutex> lck(mtx);
        std::shared_ptr<ComplexType> rv(m_q.front());
        m_q.pop();
        return rv;
    }
};

那么我们就有了使用它的代码:

SyncQueue q;

// Thread 1, Producer:
std::shared_ptr<ComplexType> ct(new ComplexType);
ct->foo = 3;
q.push(ct);

// Thread 2, Consumer:
std::shared_ptr<ComplexType> ct(q.pop());
std::cout << ct->foo << std::endl;

ct-&gt;foo 被打印时,我能保证得到3 吗? mtx 为指针本身提供了happens-before 语义,但我不确定这对ComplexType 的内存有什么影响。如果得到保证,是否意味着每个互斥锁 (std::lock_guard&lt;std::mutex&gt; lck(mtx);) 都会强制对任何已修改的内存位置进行完全缓存失效,直到独立内核的内存层次结构合并的地方?

【问题讨论】:

  • 呃,如果线程 2 在线程 1 之前获得互斥锁怎么办?
  • 任何写入内存的数据都会导致缓存行失效,但我不知道你会说什么“强制完全缓存失效”
  • @Brian - 是的,正确 - 假设顺序如上所示。
  • 这个对较早问题的回答表明,是的,如果硬件需要,互斥函数会发出内存屏障指令:stackoverflow.com/a/24143387/1401351
  • ct-&gt;foo = 3happens-before std::cout &lt;&lt; ct-&gt;foo,因此后者可以保证观察到前者的副作用。分配在q.push(ct) 之前排序,在q.pop() 之前发生,在对ct-&gt;foo 的读取访问之前排序。 “happens-before”关系在“happens-before”和“sequenced-before”边上是可传递封闭的。 Cashes 和 cores 是不相关的实现细节。

标签: c++ multithreading c++11 caching mutex


【解决方案1】:

std::mutex() 符合 Mutex 要求 (http://en.cppreference.com/w/cpp/concept/Mutex)

同一个互斥锁上的先前 m.unlock() 操作与 this 同步 锁操作(相当于释放-获取std::memory_order)

release-acquire 在这里解释 (http://en.cppreference.com/w/cpp/atomic/memory_order)

发布-获取排序

如果线程 A 中的原子存储被标记 memory_order_release 和线程 B 中的原子加载 变量被标记为 memory_order_acquire,所有内存写入(非原子 和放松的原子)发生在原子存储之前 线程 A 的观点,在线程 B 中成为可见的副作用, 也就是说,一旦原子加载完成,线程 B 就保证 查看线程 A 写入内存的所有内容。

同步是 仅在释放和获取相同的线程之间建立 原子变量。其他线程可以看到不同的内存顺序 访问比同步线程中的一个或两个。

本节中的代码示例与您的非常相似。所以应该保证线程 1 中的所有写入都会在 push() 中的互斥锁解锁之前发生。

当然,如果“ct->foo = 3”在实际分配发生在另一个线程中时没有任何特殊的棘手含义:)

wrt 缓存失效,来自 cppreference:

在强排序系统(x86、SPARC TSO、IBM 大型机)上, 对于大多数操作来说,release-acquire 排序是自动的。 不会为此同步发出额外的 CPU 指令 模式下,只有某些编译器优化会受到影响(例如 禁止编译器将非原子存储移过原子 存储释放或执行比原子更早的非原子加载 加载获取)。在弱序系统(ARM、Itanium、PowerPC)上, 必须使用特殊的 CPU 负载或内存栅栏指令。

所以这真的取决于架构。

【讨论】:

  • "当然如果 "ct->foo = 3" 没有任何特殊的棘手含义" 当然! :)
猜你喜欢
  • 2023-04-03
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-10-02
  • 2011-04-09
  • 1970-01-01
  • 1970-01-01
  • 2013-01-02
相关资源
最近更新 更多