- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
std::sync::atomic::AtomicBool
的所有方法进行内存排序(Relaxed、Release、Acquire、AcqRel 和 SeqCst),这是我以前没有使用过的。在什么情况下应该使用这些值?该文档使用了我不太理解的令人困惑的“加载”和“存储”术语。例如:
生产者线程改变了 Mutex
持有的一些状态,然后调用 AtomicBool
::compare_and_swap(false, true, ordering)
(合并无效),如果它交换,则将“无效”消息发布到并发队列(例如 mpsc
或 winapi PostMessage
)。消费者线程重置 AtomicBool
,从队列中读取,读取Mutex持有的状态。生产者可以使用松散排序,因为它前面有一个互斥锁,还是必须使用发布?消费者能否使用store(false, Relaxed)
,或者必须使用 compare_and_swap(true, false, Acquire)
接收来自互斥锁的变化?
如果生产者和消费者共享一个 RefCell
会怎样?而不是 Mutex
?
最佳答案
我不是这方面的专家,这真的很复杂,所以请随时批评我的帖子。正如 mdh.heydari 所指出的,cppreference.com 有 much better documentation of orderings比 Rust(C++ 具有几乎相同的 API)。
对于您的问题
您需要在生产者中使用“发布”排序并在消费者中使用“获取”排序。这确保了数据突变发生在 AtomicBool
之前。设置为真。
如果您的队列是异步的,那么消费者将需要继续尝试在循环中读取它,因为生产者可能会在设置 AtomicBool
之间被中断。并将一些东西放入队列中。
如果生产者代码可能在客户端运行之前多次运行,那么您不能使用 RefCell
因为他们可以在客户端读取数据时改变数据。否则就好了。
还有其他更好、更简单的方法来实现这个模式,但我假设你只是举个例子。
什么是订单?
不同的顺序与另一个线程在原子操作发生时看到的情况有关。编译器和 CPU 通常都允许重新排序指令以优化代码,并且排序会影响它们被允许重新排序指令的程度。
你可以一直使用 SeqCst
,这基本上保证了每个人都会看到该指令相对于其他指令,无论你把它放在什么地方,但在某些情况下,如果你指定限制较少的顺序,那么 LLVM 和 CPU 可以更好地优化你的代码。
您应该将这些排序视为应用于内存位置(而不是应用于指令)。
订购类型
轻松订购
除了对原子内存位置的任何修改之外,没有任何限制(因此它要么完全发生要么根本不发生)。如果单个线程检索/设置的值无关紧要,只要它们是原子的,这对于计数器之类的东西来说就很好。
获取订单
此约束表示在应用“获取”之后在代码中发生的任何变量读取都不能重新排序以在它之前发生。所以,在你的代码中说你读取了一些共享内存位置并获得值 X
,它在时间 T
存储在该内存位置,然后应用“获取”约束。您在应用约束后读取的任何内存位置都将具有它们在时间 T
时的值。或以后。
这可能是大多数人直观地期望发生的事情,但是因为只要 CPU 和优化器不改变结果就可以对指令重新排序,所以不能保证。
为了使“acquire”有用,它必须与“release”配对,否则不能保证另一个线程没有重新排序它应该在时间T
发生的写指令。到更早的时间。
获取读取您正在查找的标志值意味着您不会在其他地方看到一个陈旧的值,而该值实际上在释放存储到标志之前被写入更改。
发布订购
此约束表示在应用“发布”之前发生在您的代码中的任何变量写入都不能重新排序以在其之后发生。所以,在你的代码中,你写了几个共享内存位置,然后在时间 T
设置一些内存位置 t ,然后应用“发布”约束。在应用“发布”之前出现在您的代码中的任何写入都保证在它之前发生。
同样,这是大多数人直觉上会期望发生的事情,但不能保证没有限制。
如果另一个线程试图读取值 X
不使用“获取”,则不能保证看到与其他变量值变化相关的新值。因此它可以获得新值,但它可能看不到任何其他共享变量的新值。还要记住,测试很难。一些硬件在实践中不会显示一些不安全代码的重新排序,因此问题可能未被发现。
Jeff Preshing wrote a nice explanation of acquire and release semantics ,所以如果这不清楚,请阅读。
AcqRel 排序
这两者都可以 Acquire
和 Release
订购(即两个限制都适用)。我不确定何时需要这样做 - 如果某些 Release
在 3 个或更多线程的情况下可能会有所帮助, 一些 Acquire
,有些人两者都做,但我不太确定。
顺序排序
这是最具限制性的,因此也是最慢的选择。它强制内存访问以与每个线程相同的顺序发生。这需要一个 MFENCE
x86 上所有写入原子变量的指令(完整内存屏障,包括 StoreLoad),而较弱的排序则不然。 (SeqCst 加载在 x86 上不需要屏障,正如您在 this C++ compiler output 中看到的那样。)
读-修改-写访问,如原子增量或比较和交换,在 x86 上使用 lock
完成。 ed 指令,这些指令已经是满的内存屏障。如果您完全关心在非 x86 目标上编译为高效代码,那么尽可能避免 SeqCst 是有意义的,即使对于原子读-修改-写操作也是如此。 There are cases where it's needed , 尽管。
有关原子语义如何变成 ASM 的更多示例,请参阅 this larger set of simple functions on C++ atomic variables .我知道这是一个 Rust 问题,但它应该具有与 C++ 基本相同的 API。 Godbolt 可以针对 x86、ARM、ARM64 和 PowerPC。有趣的是,ARM64 具有加载-获取( ldar
)和存储-释放( stlr
)指令,因此它并不总是必须使用单独的屏障指令。
顺便说一句,默认情况下 x86 CPU 总是“强排序”的,这意味着它们总是表现得好像至少 AcqRel
模式已设置。因此,对于 x86,“排序”只会影响 LLVM 优化器的行为方式。另一方面,ARM 是弱有序的。 Relaxed
是默认设置的,允许编译器完全自由地重新排序,并且在弱排序的 CPU 上不需要额外的屏障指令。
关于std - 使用哪个 std::sync::atomic::Ordering ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30407121/
我正在开发一个小型图书馆,我需要做的一件事是让访问者访问一些数据并返回结果。 在一些较旧的 C++ 代码中,访问者需要声明一个 typedef return_type .例如,boost::stati
我正在尝试使用std:map类型的键和值制作std::any Visual Studio 2017 std::map m("lastname", "Ivanov"); std::cout (m["la
我已经在 C++ 的 map 中声明了一个集合为 std::map> .如何循环访问或打印设定值? 最佳答案 如果你知道如何迭代 std::map或 std::set单独地,您应该可以毫无问题地组合迭
如何循环? 我已经试过了: //----- code std::vector >::iterator it; for ( it = users.begin(); it != users.end();
我有两个用例。 A.我想同步访问两个线程的队列。 B.我想同步两个线程对队列的访问并使用条件变量,因为其中一个线程将等待另一个线程将内容存储到队列中。 对于用例 A,我看到了使用 std::lock_
我正在查看这两种类型特征的文档,但不确定有什么区别。我不是语言律师,但据我所知,它们都适用于“memcpy-able”类型。 它们可以互换使用吗? 最佳答案 不,这些术语不能互换使用。这两个术语都表示
我有以下测试代码,其中有一个参数 fS,它是 ofstream 的容器: #include #include #include #include int
这是这个问题的延续 c++ function ptr in unorderer_map, compile time error 我试图使用 std::function 而不是函数指针,并且只有当函数是
std::unordered_map str_bool_map = { {"a", true}, {"b", false}, {"c", true} }; 我们可以在此映射上使
我有以下对象 std::vector> vectorList; 然后我添加到这个使用 std::vector vec_tmp; vec_tmp.push_back(strDRG); vec_tmp.p
为什么 std::initializer_list不支持std::get<> , std::tuple_size和 std::tuple_element ?在constexpr中用得很多现在的表达式,
我有一个像这样定义的变量 auto drum = std::make_tuple ( std::make_tuple ( 0.3f , Ex
假设我有一个私有(private)std::map在我的类(class)里std::map 。我怎样才能将其转换为std::map返回给用户?我想要下面的原型(prototype) const std
假设我有一个私有(private)std::map在我的类(class)里std::map 。我怎样才能将其转换为std::map返回给用户?我想要下面的原型(prototype) const std
问题 我正在尝试将 lambda 闭包传递给 std::thread,它使用任意封闭参数调用任意封闭函数。 template std::thread timed_thread(Function&& f
我想创建一个模板类,可以容纳容器和容器的任意组合。例如,std::vector或 std::map ,例如。 我尝试了很多组合,但我必须承认模板的复杂性让我不知所措。我编译的关闭是这样的: templ
我有一个 std::vector>我将其分配给相同类型的第二个 vector 。 我收到这个编译器错误: /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_algob
有时候,我们有一个工厂可以生成一个 std::unique_ptr vector ,后来我们想在类/线程/你命名的之间共享这些指针。因此,最好改用 std::shared_ptr 。当然有一种方法可以
这个问题在这里已经有了答案: Sorting a vector of custom objects (14 个答案) 关闭 6 年前。 我创建了一个 vector vector ,我想根据我定义的参
我有三个类(class)成员: public: std::vector > getObjects(); std::vector > getObjects() const; privat
我是一名优秀的程序员,十分优秀!