- 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/
我将 Bootstrap 与 css 和 java 脚本结合使用。在不影响前端代码的情况下,我真的很难在css中绘制这个背景。在许多问题中,人们将宽度和高度设置为 0%。但是由于我的导航栏,我不能使用
我正在用 c 编写一个程序来读取文件的内容。代码如下: #include void main() { char line[90]; while(scanf("%79[^\
我想使用 javascript 获取矩阵数组的所有对 Angular 线。假设输入输出如下: input = [ [1,2,3], [4,5,6], [7,8,9], ] output =
可以用pdfmake绘制lines,circles和other shapes吗?如果是,是否有documentation或样本?我想用jsPDF替换pdfmake。 最佳答案 是的,有可能。 pdfm
我有一个小svg小部件,其目的是显示角度列表(参见图片)。 现在,角度是线元素,仅具有笔触,没有填充。但是现在我想使用一种“内部填充”颜色和一种“笔触/边框”颜色。我猜想line元素不能解决这个问题,
我正在为带有三角对象的 3D 场景编写一个非常基本的光线转换器,一切都工作正常,直到我决定尝试从场景原点 (0/0/0) 以外的点转换光线。 但是,当我将光线原点更改为 (0/1/0) 时,相交测试突
这个问题已经有答案了: Why do people write "#!/usr/bin/env python" on the first line of a Python script? (22 个回
如何使用大约 50 个星号 * 并使用 for 循环绘制一条水平线?当我尝试这样做时,结果是垂直(而不是水平)列出 50 个星号。 public void drawAstline() { f
这是一个让球以对角线方式下降的 UI,但球保持静止;线程似乎无法正常工作。你能告诉我如何让球移动吗? 请下载一个球并更改目录,以便程序可以找到您的球的分配位置。没有必要下载足球场,但如果您愿意,也可以
我在我的一个项目中使用 Jmeter 和 Ant,当我们生成报告时,它会在报告中显示 URL、#Samples、失败、成功率、平均时间、最短时间、最长时间。 我也想在报告中包含 90% 的时间线。 现
我有一个不寻常的问题,希望有人能帮助我。我想用 Canvas (android) 画一条 Swing 或波浪线,但我不知道该怎么做。它将成为蝌蚪的尾部,所以理想情况下我希望它的形状更像三角形,一端更大
这个问题已经有答案了: Checking Collision of Shapes with JavaFX (1 个回答) 已关闭 8 年前。 我正在使用 JavaFx 8 库。 我的任务很简单:我想检
如何按编号的百分比拆分文件。行数? 假设我想将我的文件分成 3 个部分(60%/20%/20% 部分),我可以手动执行此操作,-_-: $ wc -l brown.txt 57339 brown.tx
我正在努力实现这样的目标: 但这就是我设法做到的。 你能帮我实现预期的结果吗? 更新: 如果我删除 bootstrap.css 依赖项,问题就会消失。我怎样才能让它与 Bootstrap 一起工作?
我目前正在构建一个网站,但遇到了 transform: scale 的问题。我有一个按钮,当用户将鼠标悬停在它上面时,会发生两件事: 背景以对 Angular 线“扫过” 按钮标签颜色改变 按钮稍微变
我需要使用直线和仿射变换绘制大量数据点的图形(缩放图形以适合 View )。 目前,我正在使用 NSBezierPath,但我认为它效率很低(因为点在绘制之前被复制到贝塞尔路径)。通过将我的数据切割成
我正在使用基于 SVM 分类的 HOG 特征检测器。我可以成功提取车牌,但提取的车牌除了车牌号外还有一些不必要的像素/线。我的图像处理流程如下: 在灰度图像上应用 HOG 检测器 裁剪检测到的区域 调
我有以下图片: 我想填充它的轮廓(即我想在这张图片中填充线条)。 我尝试了形态学闭合,但使用大小为 3x3 的矩形内核和 10 迭代并没有填满整个边界。我还尝试了一个 21x21 内核和 1 迭代,但
我必须找到一种算法,可以找到两组数组之间的交集总数,而其中一个数组已排序。 举个例子,我们有这两个数组,我们向相应的数字画直线。 这两个数组为我们提供了总共 7 个交集。 有什么样的算法可以帮助我解决
简单地说 - 我想使用透视投影从近裁剪平面绘制一条射线/线到远裁剪平面。我有我认为是使用各种 OpenGL/图形编程指南中描述的方法通过单击鼠标生成的正确标准化的世界坐标。 我遇到的问题是我的光线似乎
我是一名优秀的程序员,十分优秀!