指针地址交换总是在C ++中进行原子操作吗?

时间:2014-02-26 10:20:17

标签: c++ windows multithreading

关于这个问题Is there cases where a 32-bit variable could not been properly-aligned以及提供的答案,在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;
};

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

2 个答案:

答案 0 :(得分:9)

C ++标准

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

C ++标准说如果一个线程修改了内存位置而另一个线程修改/访问了相同的内存位置,则存在数据争用,如果存在这样的数据争用,则程序会受到未定义行为的影响

  

[<强> intro.multithread

     

4)两个表达式评估冲突如果其中一个修改了内存位置(1.7),另一个访问或修改了相同的内存位置。

     

...

     

21)程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且在其他。任何此类数据争用都会导致未定义的行为。

1。 raw 未包含在std::atomic<Foo*>或等效内容中。


特定于实施的行为(Windows 32/64位)

在Windows下,它保证对正确对齐的 32-bit变量的读/写始终是 atomic ,如{{3由您之前引用的the article链接。

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

  

互锁变量访问 - question/answer

     

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

     

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


这是什么意思?

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

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


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

如果你通过&#34;交换&#34; 意味着一个操作,如下面的代码片段中写的内容,答案是,但如果你的意思是< em>&#34;分配&#34; 答案是

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位变量是安全的,但在上面没有任何内容可以保证xy都没有值2当我们处于swap

的中间时

但是,保证x永远不会包含先前x中50%的字节和y中50%的字节,或类似的...个人写作是原子的。

答案 1 :(得分:1)

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

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

鉴于所有这些,你最好使用std::atomic类型,或者如果你没有访问C ++ 11那么应该有一些互锁的读/写函数可用。