- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
这是一个关于 std::memory_order
的问题C++11 中的规则,当涉及到三个线程时。比如说,一个线程 producer 保存一个值并设置一个标志。然后,另一个线程 relay 在设置另一个标志之前等待这个标志。最后,第三个线程 consumer 等待来自 relay 的标志,这应该表明 data
已准备好供消费者使用。
这是一个最小程序,采用 C++ 引用 (http://en.cppreference.com/w/cpp/atomic/memory_order) 中示例的样式:
#include <thread>
#include <atomic>
#include <cassert>
std::atomic<bool> flag1 = ATOMIC_VAR_INIT(false);
std::atomic<bool> flag2 = ATOMIC_VAR_INIT(false);
int data;
void producer()
{
data = 42;
flag1.store(true, std::memory_order_release);
}
void relay_1()
{
while (!flag1.load(std::memory_order_acquire))
;
flag2.store(true, std::memory_order_release);
}
void relay_2()
{
while (!flag1.load(std::memory_order_seq_cst))
;
flag2.store(true, std::memory_order_seq_cst);
}
void relay_3()
{
while (!flag1.load(std::memory_order_acquire))
;
// Does the following line make a difference?
data = data;
flag2.store(true, std::memory_order_release);
}
void consumer()
{
while (!flag2.load(std::memory_order_acquire))
;
assert(data==42);
}
int main()
{
std::thread a(producer);
std::thread b(relay_1);
std::thread c(consumer);
a.join(); b.join(); c.join();
}
第一个函数relay_1()
不够,可以触发 assert
在消费者中。根据上面引用的 C++ 引用,memory_order_acquire
关键字“确保释放相同原子变量的其他线程中的所有写入在当前线程中可见”。所以,data=42
在设置 flag2
时对 relay 可见.它设置为 memory_order_release
,它“确保当前线程中的所有写入在获取相同原子变量的其他线程中可见”。然而,data
没有被relay触及,所以消费者可能会看到不同顺序的内存访问,data
当消费者看到flag2==True
时可能未初始化.
同样的论点适用于 relay_2()
中更严格的内存排序。 .顺序一致的排序意味着“同步是在标记为 std::memory_order_seq_cst
的所有原子操作之间建立的” ”。但是,这并没有说明变量 data
.
还是我理解错了这里和relay_2()
够了吗?
让我们通过访问 data
来解决这个问题如 relay_3()
.在这里,行 data = data
暗示data
在 flag1
之后阅读去了true
, relay 线程写入 data
, 在设置 flag2
之前.因此,消费者线程必须看到正确的值。
但是,这个解决方案似乎有点奇怪。线路data = data
似乎是编译器会(在顺序代码中)立即优化掉的东西。
虚拟线在这里起作用吗?使用 C++11 实现跨三个线程同步的更好方法 std::memory_order
特点?
顺便说一下,这不是一个学术问题。想象一下 data
是一大块数据而不是单个整数,第 i 个线程需要将信息传递给第 (i+1) 个线程,直到索引≤i的所有线程处理了数据的哪个元素。
阅读 Michael Burr 的回答后很明显 relay_1()
足够了。请阅读他的帖子以获得完全令人满意的问题解决方案。 C++11 标准提供了比仅从 cppreference.com 网站推断出的保证更严格的保证。因此,请将 Michael Burr 的帖子中的论点视为权威,而不是我上面的评论。要走的路是在所讨论的事件之间建立一个“线程间发生之前”的关系(这是传递性的)。
最佳答案
我认为 relay_1()
足以将值 42
从生产者通过 data
传递给消费者。
为了说明这一点,首先我会为感兴趣的操作提供单字母名称:
void producer()
{
/* P */ data = 42;
/* Q */ flag1.store(true, std::memory_order_release);
}
void relay_1()
{
while (/* R */ !flag1.load(std::memory_order_acquire))
;
/* S */ flag2.store(true, std::memory_order_release);
}
void consumer()
{
while (/* T */ !flag2.load(std::memory_order_acquire))
;
/* U */ assert(data==42);
}
我将使用符号 A -> B
表示“线程间发生在 B 之前”(C++11 1.10/11)。
我认为 P
是 U
的可见副作用,因为:
P
排在Q
之前,R
排在S
之前,T
排在 U
(1.9/14)Q
与R
同步,S
与T
同步 (29.3/2)“线程间发生在之前”(1.10/11) 的定义支持以下所有要点:
Q -> S
因为标准说“线程间发生在评估 B 之前,如果......对于某些评估 X,A 与 X 同步并且 X 在 B 之前排序"(Q
与 R
同步,R
排在 S
之前,所以 Q -> S
)
S -> U
遵循类似的逻辑(S
与 T
同步并且 T
被排序在 U
之前,所以 S -> U
)
Q -> U
因为 Q -> S
和 S -> U
(“线程间发生在评估 B if ... A 线程间发生在 X 之前,X 线程间发生在 B 之前)
最后,
P -> U
因为 P
在 Q
和 Q -> U
之前排序(“A inter -线程发生在评估 B 之前,如果 ... A 在 X 之前排序,并且 X 线程间发生在 B 之前")由于 P
线程间发生在 U
之前,P
发生在 U
之前 (1.10/12) 并且P
是相对于 U
(1.10/13) 的“可见副作用”。
relay_3()
也足够了,因为 data=data
表达式是无关紧要的。
对于这个生产者/消费者问题,relay_2()
至少和 relay_1()
一样好,因为在存储操作中 memory_order_seq_cst
是一个释放,在加载操作中 memory_order_seq_cst
是一个获取(参见 29.3/1)。所以可以遵循完全相同的逻辑。使用 memory_order_seq_cst
的操作有一些额外的属性,这些属性与所有 memory_order_seq_cst
在其他 memory_order_seq_cst
操作中如何排序有关,但这些属性不会出现玩这个例子。
我认为如果没有像这样的传递行为,memory_order_acquire
和 memory_order_release
将不会对实现更高级别的同步对象非常有用。
关于c++ - std::memory_order 和三个线程的同步谜题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16115014/
这个问题在这里已经有了答案: With arrays, why is it the case that a[5] == 5[a]? (20 个答案) 关闭 7 年前。 我正在尝试解开这个谜团: in
很难为这个问题找到合适的标题。欢迎编辑! :) 这是我的来源xml代码: [color hex] [color hex] [color he
编辑:看起来像一个索引问题,在问题底部更新 我有以下查询 + 子查询,其结果我无法解释。我从这个最小的输入数据集开始(这里的应用程序正在捕获数据变化,PK 是 id + tx_id)。 mysql>
基本上,我在这里试图实现的是让全局变量具有指向结构的指针数组,其大小在编译时是未知的——在我下面的示例中,它是 my_struct **tab。在最终版本中,我想调用一个 JNI 方法来初始化我的指针
所以我想弄清楚这个谜题: function fun1(){ var result = []; for (var i = 0; i < 5; i++){ result.p
一群 child 围成一圈。选择第一个 child ,他们从那个 child 开始顺时针计数,直到达到固定数字(n,在游戏开始时给出)。当计数达到 n 时,第 n 个位置的 child 被淘汰。游戏从
(我是 JS 新手,所以请耐心等待)我正在使用 table 来构建滑动益智游戏。我需要一个可以扰乱值的函数,但我不确定应该如何让它显示在表格单元格中。现在我的代码只是按顺序显示数字。 我有两个函数 -
我有一个 UserForm,xForm,它在类模块(假设为 TestClass)中实例化为: 'TestClass Dim Form as New xForm Private WithEvents E
如果没有循环或游标,如何获取日期间隔列表并将它们转换为 1 和 0 的字符串,这样: 每一位代表从 min(所有日期)到 max(所有日期)的每一天 如果该天属于任何日期间隔,则该位为 1 如果该天不
我读过很多A*算法的伪代码,但它们都没有真正解释如何输出解。我相信我理解使用优先级队列表示尚未访问的内容和使用已探索的表的概念,但是当我执行该算法时,我不知道在什么时候打印出结果。有没有人有一个伪代码
以下两个查询不会返回相同的结果。为什么? 注意 :我发现这个问题是一个 Mysql 难题,我没有关于这个问题的更多数据? SELECT table1.* FROM table1 LEFT JOIN t
如果您对我应该如何在 python 中执行以下任务有任何建议,我正在徘徊:假设我有以下类(class): class A(object): self._classes = [] def
我很难理解如何在不引起内存分配问题的情况下解决这个问题。我很确定我的逻辑是合理的,但不幸的是,这似乎还不够好。有没有人对此有任何建议,以便我了解如何更有效地编写代码? 问题来了:示例输入:1个5 2
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 8 年前。 Improve
所以这是一个难题: “kb”是扩展 java.util.Hashtable 的类的实例键是一个字符串,存储的值是一个名为“IntelCard”的类 此代码提取 key ,并尝试从表中打印数据
我有以下难题要解决,但我不确定我该怎么做。它说: 有一个 Ubuntu Linux C 程序可以输出变量的地址。 v1: 0xa156128 v2: 0xff97410c v3: 0xf750e34b
我有一个简单的 HTML 页面如下:- Col1 Col2
大家好, 我尝试了八个难题的解决方案发布 here由 joel Neely 玩弄并修改它,以便可以用来解决更高的网格[将网格的字符串表示更改为二维整数表示并修改相应的逻辑]。然而,修改后的代码可以解决
我正在尝试解决下文详述的 projecteuler 难题。我当前的函数适用于数字 1 到 10,但是当我尝试 1 到 20 时,它会一直循环下去而没有结果。 2520 is the smallest
我在为基于图 block 的游戏编写随机关卡生成器时遇到了一个有趣的问题。我已经为它实现了一个强力求解器,但它的速度呈指数级下降,而且绝对不适合我的用例。我不一定要寻找完美的解决方案,我会对性能良好的
我是一名优秀的程序员,十分优秀!