在C++中写入相同值的竞争条件?
Race Condition with writing same value in C++?
当操作写入单个常量值时,代码中存在竞争条件是否有任何问题?例如,如果有一个并行循环为另一个数组中的每个值填充了一个seen
数组arr
(假设越界索引没有问题(。关键部分可以是以下代码:
//parallel body with index i
int val = arr[i];
seen[val] = true;
由于写入的唯一值是true
这是否使得不需要互斥锁,并且可能损害性能?即使线程相互踩踏,它们也只是用相同的值填充地址,对吗?
C++内存模型不会为您提供写入相同值的免费通行证。
如果两个线程在没有同步的情况下写入非原子对象,那只是争用条件。 争用条件意味着程序执行未定义的行为。 在程序执行过程中的任何地方发生的未定义行为意味着程序的行为,无论是在未定义行为点之前还是之后,都不受C++标准的任何限制。
给定的编译器可以自由地提供更自由的内存模型。 我不知道有没有这样做。
您必须了解的一件事是,C++不是汇编宏语言。 它不必产生你在脑海中想象的天真的汇编程序。 相反,C++试图使编译器易于生成汇编程序,这是非常不同的事情。
编译器可以并且确实确定"如果 X 发生,我们会得到未定义的行为;因此,在生成代码时,我将围绕 X 不会发生的事实进行优化。 在这种情况下,编译器可以证明具有定义行为的程序可以在两个不同的不同步线程中具有相同的val
。
所有这些都可以在生成任何程序集之前很久就发生。
在程序集级别,某些硬件可能会通过未对齐的多字节值赋值来做一些有趣的事情。 一些硬件可以(理论上;我不知道在实践中有任何(当声称是单线程写入的指令发生在相同字节上的两个不同内核中时,会引发陷阱。
所以这是C++的UB。 一旦你有了UB,你必须在编译器可以看到的任何地方审核程序生成的汇编代码。 如果你做LTO,这意味着在你的整个程序中,至少在调用或与你的代码进行UB交互的任何地方,距离不明确。
只需编写定义的行为。 只有当这被证明是关键任务性能瓶颈时,您才应该花更多的精力来优化它(首先更快地定义行为,并且只有在失败时,您才会考虑 UB(。
可能存在依赖于体系结构的约束,要求将看到的数组元素分隔一定数量,以防止竞争线程破坏在同一机器字(甚至缓存行(中冲突的值。
也就是说,如果seen
定义为bool seen[N];
则seen
长度为 N 个字节,并且每个元素都与其邻居直接相邻。如果一个线程更改元素 0,另一个线程更改元素 2,则这两个更改都发生在同一个 64 位机器字中。如果这两个更改由不同的内核(甚至在多 CPU 系统的不同 CPU 上(同时进行,它们将尝试将冲突作为整个 64 位机器字(在某些情况下更大(来解决。这样做的结果将是,写入的true
之一将被获胜线程更新到相邻元素时恢复到其先前的状态(可能false
(。
相反,如果您将 seen 定义为一个结构数组,每个结构都与缓存行一样大,那么您可能会让竞争线程在该结构中混合一个布尔值......但这是有风险的,因为并非所有 CPU 都会共享相同的缓存冲突验证策略、行大小等......不可避免地,会有一个 CPU 出现故障。
- compare_exchange C++函数如何确定竞争条件?
- 在C++中写入相同值的竞争条件?
- QByteArray 通过队列连接按值发出并连接并附加到竞争条件?
- 从stdin读取时子进程挂起(fork/dup2竞争条件)
- 如何修复条件变量等待/通知的竞争条件
- 在没有互斥锁的情况下重新计数时如何避免竞争条件?
- 替代 rand() 以避免竞争条件?
- CUDA 内核中的竞争条件
- 为什么跨线程更改共享变量的代码显然没有受到竞争条件的影响
- 类声明自己(*this)为private以避免竞争条件/放弃在gcc中对threadprivate的请求
- 使用 gtest EXPECT_CALL 时竞争条件段错误,而另一个期望是执行相同的方法
- boost::进程间消息队列创建时的竞争条件
- 如何在没有竞争条件的情况下将 QFutureWatcher 与 QtConcurrent::run() 一起使用
- 为什么 printf 可以屏蔽竞争条件,而系统日志不能?
- 为什么这个代码会产生竞争条件
- 为什么 CUDA 同步点不能阻止竞争条件?
- 竞争条件:一个线程创建静态对象,另一个线程在初始化完成之前使用它.如何处理
- 如何使用 Boost Atomic 删除竞争条件
- boost::mutex 无法帮助避免C++程序中的竞争条件
- 类中的递归函数或竞争条件