例如,如果我们有两个std::atomic
,并且想先读取值,然后再标记第二个,则我们不再需要first的值。我们不希望对这些操作进行重新排序(否则在读取前可以重写第一个值),但是操作之间没有数据依赖性,因此我们明确需要一个障碍来防止重新排序(并且memory_order_consume
不能这样做)不适合)。
在这里,完全的围栏肯定是多余的。同样,我们既不需要释放也不需要语义(即使它们提供了这种障碍)。我们所需要做的只是保持读写操作的顺序。
有一些便宜的篱笆可以满足我们的需求吗?
编辑:我需要的示例。
std::atomic<X> atomicVal;
std::atomic<bool> atomicFlag = false;
...
auto value = atomicVal.load(std::memory_order_relaxed);
some_appropriative_fence();
atomicFlag.store(true, std::memory_order_relaxed);
在设置了atomicFlag
之后,atomicVal
可以被覆盖为其他值,因此我们需要先阅读它。
我们当然可以做
auto value = atomicVal.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
atomicFlag.store(true, std::memory_order_relaxed);
但是对于我们需要的操作来说太昂贵了。
我很有趣的是,最小的围栏足以保证操作顺序。
答案 0 :(得分:1)
更新后: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering
您希望使用以下方式写入(存储)原子标志和变量:
ptr.store(p, std::memory_order_release);
,并且您希望通过以下方式读取标志和值:
p2 = ptr.load(std::memory_order_acquire)
这似乎是它们存在的确切原因。
编辑2:实际上,发布消费可能会更好。但是我从来没有看到它使用过。上面的链接还指出:
Note that currently (2/2015) no known production compilers track dependency chains: consume operations are lifted to acquire operations.
编辑3:示例代码所做的事情类似于我所了解的。
#include <thread>
#include <iostream>
#include <atomic>
std::atomic<int> x;
std::atomic<int> y;
auto write_op = std::memory_order_release;
auto read_op = std::memory_order_acquire;
// auto write_op = std::memory_order_seq_cst;
// auto read_op = std::memory_order_seq_cst;
void consumer()
{
while(true)
{
int rx,ry;
do
{
ry = y.load(read_op); // flag read first to guarantee x validity
rx = x.load(read_op);
}
while(ry == 0); // wait for y. y acts as the flag, here
if (ry == -1)
{
break;
}
if (rx != ry) // check consistency
{
std::cout << "Boo " << rx << " " << ry << std::endl;
}
x.store(0, write_op);
y.store(0, write_op);
}
}
void producer()
{
int count = 0;
int steps = 0;
while(steps < 50)
{
while(y.load(read_op) != 0) {} // wait for y to have been consumed
int value = std::rand() % 10 + 1;
x.store(value, write_op); // stores values
y.store(value, write_op); // indicates readiness to other thread
count++;
if (count == 1000000)
{
std::cout << '.' << std::endl;
count = 0;
steps++;
}
}
y.store(-1);
}
int main()
{
x = 0;
y = 0;
std::thread thread1(producer);
std::thread thread2(consumer);
thread1.join();
thread2.join();
}