5
#include <atomic>
#include <iostream>
#include <thread>

class atomicAcquireRelease00
{
public:
    atomicAcquireRelease00() : x(false), y(false), z(0) {}
    void run()
    {
        std::thread a(&atomicAcquireRelease00::write_x, this);
        std::thread b(&atomicAcquireRelease00::write_y, this);
        std::thread c(&atomicAcquireRelease00::read_x_then_y, this);
        std::thread d(&atomicAcquireRelease00::read_y_then_x, this);

        a.join();
        b.join();
        c.join();
        d.join();

        std::cout<<"z == "<<z.load()<<std::endl;
    }

private:
    void write_x()
    {                
        x.store(true, std::memory_order_release); //(1)
    }

    void write_y()
    {           
        y.store(true, std::memory_order_release); //(2)
    }

    void read_x_then_y()
    {            
        while(!x.load(std::memory_order_acquire)); //(3)
        if(y.load(std::memory_order_acquire)){    //(4)
            ++z;
        }
    }

    void read_y_then_x()
    {           
        while(!y.load(std::memory_order_acquire));  //(5)
        if(x.load(std::memory_order_acquire)){      //(6)
            ++z;
        }

    }

private:
    std::atomic<bool> x, y;
    std::atomic<int> z;
};            

int main()
{
   for(size_t i = 0; i != 50; ++i){
     atomicAcquireRelease00().run();
   }          

  return 0;
}

atomicAcquireRelease00加载值时不遵守顺序。据我所知,如果我将操作存储为std::memory_order_release 和操作加载为std::memory_order_acquire一对声明,则加载和存储在同一个原子变量上的操作将同步,但这个简单的示例并没有像我预期的那样工作。

这个过程基于我的想象

  • 案例一:

    1. x 设置为真
    2. 在 (4) 处,y.load 返回 false,z 保持为零
    3. y 设置为真
    4. 在 (6) 之后,z 变为 1
  • 案例 B:

    1. y 设置为真
    2. 在 (6) 处,x.load 返回 false,z 保持为零
    3. x 设置为真
    4. 在 (4) 之后,z 变为 1
  • 案例 C:

    1. x 设置为真,y 设置为真
    2. 在 (4) 和 (6) 之后,z 变为 2

我不能保证x或者y会先设置为true,但是当x设置为true时,负载x应该与之同步以及y,什么样的情况会z保持为零?

4

2 回答 2

9

正是Anthony Williams 的“Concurrency In Action”中的示例清单 5.7。

他解释说:

在这种情况下,断言可以触发(就像在宽松排序的情况下一样),因为 load ofx和 load of yto read都是可能的falsex 和 y 由不同的线程写入,因此在每种情况下从释放到获取的顺序对其他线程中的操作没有影响。

在此处输入图像描述

于 2013-09-07T19:57:16.160 回答
2

C/C++11 不保证存储到不同内存位置的总顺序,除非您使用 seq_cst。所以每个线程都可以自由地以不同的顺序查看商店。获取-释放同步没有帮助,因为它只是从执行存储的线程到执行加载的线程。如果您想玩这样的单元测试并更好地发展您的直觉,请尝试 CDSChecker。它是一个工具,可以向您展示 C11 的任何实际实现可能产生的所有行为。

于 2013-09-08T07:37:17.707 回答