指针地址交换总是C++中的原子操作吗

Is pointer address swapping always an atomic operation in C++?

本文关键字:原子操作 C++ 地址 交换 指针      更新时间:2023-10-16

关于这个问题,是否存在32位变量无法正确对齐和提供答案的情况,我可以假设在windows平台下工作时可以交换地址而不会产生任何副作用吗?

例如:

struct Foo
{
// whatever Foo can hold
};
struct Bar
{
void buildFoo()
{
auto tmp = new Foo;
// do some stuff on tmp, or not
foo = tmp;
}
Foo* foo;
};

现在,让一些线程通过Bar的实例使用foo,而让其他线程调用Bar::buildFoo()会产生什么后果?

C++标准

,同时修改/访问原始2指针不能保证是c++中的原子操作。

C++标准规定,如果一个线程修改一个内存位置,而另一个线程则修改/访问同一内存位置,则存在数据竞争,如果存在这种数据竞争,则程序会出现未定义的行为

[intro.多线程]

4)如果其中一个表达式修改内存位置(1.7),而另一个表达式访问或修改同一内存位置,则两个表达式求值冲突

21)如果程序在不同的线程中包含两个冲突的操作,则程序的执行包含数据竞赛,其中至少一个操作不是原子操作,并且两个操作都不在另一个之前发生。任何这样的数据竞争都会导致未定义的行为。

1。 raw未包装在std::atomic<Foo*>或等效物中


实现特定行为(windows 32/64位)

在windows下,可以保证对正确对齐的32-bit变量的读/写始终是原子,正如您前面引用的问题/答案链接的文章所述。

对正确对齐的64-bit变量的访问在64-bit窗口上也是原子的。

联锁变量访问-http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx

对正确对齐的32位变量的简单读取和写入是原子操作。换句话说,您最终不会只更新变量的一部分;以原子方式更新所有比特。

但是,不能保证访问是同步的。如果两个线程从同一个变量进行读取和写入,则无法确定一个线程是否会在另一个线程执行写入操作之前执行读取操作。


这意味着什么

标准说明了一件事,而微软的文档说明了另一件事。。我们应该信任和合作哪些?这当然取决于我们在做什么。

如果我们是为windows平台开发soley,我们可以阅读所使用的编译器在代码生成方面的保证,然后从那里开始,但如果我们想编写可能在不同平台下编译和运行的代码,唯一需要正确信任的就是标准。


所以在windows下工作时,我可以安全地交换32位变量

如果您通过"交换">意味着一个操作,比如下面的代码段中写的内容,答案是No,但如果你的意思是"分配">答案是

void swap (int& a, int& b) {
int tmp = a;

a = b;
b = tmp;
}
int main () {
int x = 1;
int y = 2;
swap (x, y);
}

上面的代码片段(或前面提到的文档)中没有任何内容表明这将是一个原子操作,当查看swap的实现时,我们很容易发现这些操作没有正确同步。

在windows下,读取/写入单个32位变量是安全的,但在上面的内容中,当我们处于swap的中间时,没有任何内容可以保证xy都不具有值2

然而,可以保证x永远不会由前一个x中50%的字节和y中50%的比特组成,或类似。。个人写作是原子性的。

设置foo可能是原子的,但它是特定于实现的。原因是大多数平台在一条指令中写入一个字大小的地址,而这是一个原子操作。然而,没有要求您的平台将指针存储在一个单词大小的位置,尽管很少不这样做。

您还需要考虑指令的重新排序,以及其他线程可能在一段时间内看不到对foo的写入的事实——指针的位置可能保存在处理器缓存中,而不会刷新到内存中。

考虑到所有这些,您最好使用std::atomic类型,或者如果您没有访问C++11的权限,那么应该有一些互锁的读/写功能可用。