一般地,对于int num,num++(或++num),作为读-修改-写操作中,是不是原子.但我经常看到编译器,例如GCC,为它生成以下代码(在这里尝试):
由于第5行对应于num++一条指令,我们可以得出结论,在这种情况下num++ 是原子的吗?
如果是这样,是否意味着如此生成num++可以在并发(多线程)场景中使用而没有任何数据争用的危险(例如,我们不需要制作它,std::atomic<int>并强加相关成本,因为它是无论如何原子)?
UPDATE
请注意,这个问题不是增量是否是原子的(它不是,而且是问题的开头行).它是否可以在特定场景中,即在某些情况下是否可以利用单指令性质来避免lock前缀的开销.而且,作为公认的答案约单处理器的机器,还有部分提到这个答案,在其评论和其他人谈话解释,它可以(尽管不是C或C++).
全局变量在2个不同核心上的2个并发运行线程之间共享.线程写入和读取变量.对于原子变量,一个线程可以读取过时值吗?每个核心可能在其缓存中具有共享变量的值,并且当一个线程在缓存中写入其副本时,不同核心上的另一个线程可能从其自己的缓存中读取过时值.或者编译器执行强大的内存排序以从其他缓存中读取最新值?c ++ 11标准库具有std :: atomic支持.这与volatile关键字有何不同?在上述场景中,volatile和atomic类型的行为方式有何不同?
我想知道为什么没有编译器准备将相同值的连续写入合并到单个原子变量,例如:
#include <atomic>
std::atomic<int> y(0);
void f() {
auto order = std::memory_order_relaxed;
y.store(1, order);
y.store(1, order);
y.store(1, order);
}
Run Code Online (Sandbox Code Playgroud)
我尝试过的每个编译器都会发出三次上面的编写.什么合法的,无种族的观察者可以看到上述代码与具有单次写入的优化版本之间的差异(即,不是"假设"规则适用)?
如果变量是易变的,那么显然不适用优化.在我的情况下有什么阻止它?
这是编译器资源管理器中的代码.
我研究了Java内存模型并看到了重新排序问题.一个简单的例子:
boolean first = false;
boolean second = false;
void setValues() {
first = true;
second = true;
}
void checkValues() {
while(!second);
assert first;
}
Run Code Online (Sandbox Code Playgroud)
重新排序是非常不可预测和奇怪的.此外,它破坏了抽象.我认为处理器架构必须有充分的理由去做一些对程序员来说太不方便的事情. 这些原因是什么?
有很多关于如何处理重新排序的信息,但我找不到任何关于它为什么需要的信息.在任何地方,人们只会说"这是因为一些性能优势".例如,second之前存储的性能优势是first什么?
您能推荐一些关于此的文章,论文或书籍,或者自己解释一下吗?
java optimization multithreading cpu-architecture compiler-optimization
考虑一个原子读-修改-写操作,例如x.exchange(..., std::memory_order_acq_rel)。出于对其他对象的加载和存储进行排序的目的,这是否被视为:
具有获取-释放语义的单个操作?
或者,作为一个获取加载,然后是一个释放存储,附加保证其他加载和存储x将同时观察它们或两者都不观察?
如果它是 #2,那么尽管在加载之前或存储之后不能对同一线程中的其他操作进行重新排序,但仍然存在在两者之间重新排序的可能性。
作为一个具体的例子,考虑:
std::atomic<int> x, y;
void thread_A() {
x.exchange(1, std::memory_order_acq_rel);
y.store(1, std::memory_order_relaxed);
}
void thread_B() {
// These two loads cannot be reordered
int yy = y.load(std::memory_order_acquire);
int xx = x.load(std::memory_order_acquire);
std::cout << xx << ", " << yy << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
可以thread_B输出0, 1吗?
如果x.exchange()换成了x.store(1, std::memory_order_release);那么thread_B肯定能输出0, 1。是否应该exchange()排除额外的隐式负载?
cppreference听起来像 #1 是这种情况并且0, 1被禁止:
具有此内存顺序的读-修改-写操作既是获取操作又是释放操作。当前线程中的任何内存读取或写入都不能在此存储之前或之后重新排序。
但是我在标准中找不到任何明确的内容来支持这一点。实际上,该标准对原子读-修改-写操作几乎没有说明,除了 N4860 …
c++ ×4
atomic ×2
c++11 ×2
stdatomic ×2
assembly ×1
c ×1
concurrency ×1
java ×1
optimization ×1