C++ 如何制作轻型负载存储屏障
例如,如果我们有两个C++ 如何制作轻型负载存储屏障,c++,multithreading,concurrency,memory-barriers,C++,Multithreading,Concurrency,Memory Barriers,例如,如果我们有两个std::atomics,并且希望从第一个读取值,然后将第二个标记为我们不再需要第一个的值。我们不希望这些操作被重新排序(否则第一个值可以在我们读取它之前被重写),但是操作之间没有数据依赖性,所以我们确实需要一个屏障来防止重新排序(而且内存顺序\u消耗不适合) 这里的篱笆太满了。此外,我们既不需要释放也不需要获取语义(即使它们提供了这样的障碍)。我们所需要的只是保持读写操作的顺序 有没有便宜的篱笆可以满足我们的需要 编辑:我需要的例子 std::atomic<X>
std::atomic
s,并且希望从第一个读取值,然后将第二个标记为我们不再需要第一个的值。我们不希望这些操作被重新排序(否则第一个值可以在我们读取它之前被重写),但是操作之间没有数据依赖性,所以我们确实需要一个屏障来防止重新排序(而且内存顺序\u消耗不适合)
这里的篱笆太满了。此外,我们既不需要释放也不需要获取语义(即使它们提供了这样的障碍)。我们所需要的只是保持读写操作的顺序
有没有便宜的篱笆可以满足我们的需要
编辑:我需要的例子
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);
但这对我们需要的手术来说太贵了
我很感兴趣的是,最小的围栏足以保证操作顺序。更新后:
您希望原子标志和变量使用以下内容写入(存储):
您希望通过以下方式读取标志和值:
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();
}
#包括
#包括
#包括
std::原子x;
std::原子y;
自动写入\u op=std::内存\u顺序\u释放;
自动读取\u op=std::内存\u顺序\u获取;
//自动写入\u op=std::内存顺序\u顺序\u cst;
//自动读取=标准::内存顺序顺序cst;
无效消费者()
{
while(true)
{
int rx,ry;
做
{
ry=y.load(read_op);//首先读取标志以保证x的有效性
rx=x.负载(读取操作);
}
while(ry==0);//等待y。y作为标志,在这里
如果(ry==-1)
{
打破
}
if(rx!=ry)//检查一致性
{
std::cout为了确保您得到最佳答案,您能否发布一个代码段,其中两个变量是读/写的,预期输出和示例输出都是不受欢迎的?如果您的代码将在x86上执行,则Intel体系结构保证了这一点:“写入不会用旧的读取重新排序。”是的,但我的目标是弱内存模型,其中任何操作都可以重新排序。是的,代码> SEQJCST 当然适合,但我认为它是多余的,并且对最便宜的可能的栅栏感兴趣。那么,我怀疑你可以安全地捕获/释放。然后,当我们读一读然后写一篇文章时,我们可以考虑单释放或获取屏障,这对这两个O来说足够了。不相互重新排序的操作?如果原子标记存储在线程A中的order_release中,当线程B将order_acquire加载它时,它将保证看到原子值写入(以前已完成)。但是,要使其起作用,您必须保证在写入atomic_标志后不会发生其他atomic_val写入。通常,在读取后执行相反的过程,将标志存储回false。
Note that currently (2/2015) no known production compilers track dependency chains: consume operations are lifted to acquire operations.
#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();
}