gpt4 book ai didi

c++ - 了解 C++11 内存栅栏

转载 作者:IT老高 更新时间:2023-10-28 12:41:29 26 4
gpt4 key购买 nike

我正在尝试理解 c++11 中的内存栅栏,我知道有更好的方法可以做到这一点,原子变量等等,但想知道这种用法是否正确。我意识到这个程序没有做任何有用的事情,我只是想确保栅栏函数的使用符合我的想法。

基本上,发布确保在栅栏之前在此线程中所做的任何更改对栅栏之后的其他线程都是可见的,并且在第二个线程中,对变量的任何更改在栅栏之后的线程中立即可见?

我的理解正确吗?还是我完全没有捕获重点?

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

int a;

void func1()
{
for(int i = 0; i < 1000000; ++i)
{
a = i;
// Ensure that changes to a to this point are visible to other threads
atomic_thread_fence(std::memory_order_release);
}
}

void func2()
{
for(int i = 0; i < 1000000; ++i)
{
// Ensure that this thread's view of a is up to date
atomic_thread_fence(std::memory_order_acquire);
std::cout << a;
}
}

int main()
{
std::thread t1 (func1);
std::thread t2 (func2);

t1.join(); t2.join();
}

最佳答案

您的使用确实实际上确保了您在评论中提到的事情。也就是说,您对栅栏的使用并不能确保您对a的分配对其他线程可见,或者您从a读取的值是“最新的”。这是因为,尽管您似乎对应该在哪里使用栅栏有了基本的想法,但您的代码实际上并没有满足这些栅栏“同步”的确切要求。

这是一个不同的例子,我认为它更好地展示了正确的用法。

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

std::atomic<bool> flag(false);
int a;

void func1()
{
a = 100;
atomic_thread_fence(std::memory_order_release);
flag.store(true, std::memory_order_relaxed);
}

void func2()
{
while(!flag.load(std::memory_order_relaxed))
;

atomic_thread_fence(std::memory_order_acquire);
std::cout << a << '\n'; // guaranteed to print 100
}

int main()
{
std::thread t1 (func1);
std::thread t2 (func2);

t1.join(); t2.join();
}

原子标志上的加载和存储不同步,因为它们都使用宽松的内存排序。如果没有栅栏,这段代码将是一个数据竞争,因为我们在不同线程中执行一个非原子对象的冲突操作,如果没有栅栏和它们提供的同步,那么a

但是使用栅栏我们确实获得了同步,因为我们保证线程 2 将读取线程 1 写入的标志(因为我们循环直到看到该值),并且因为原子写入发生在释放栅栏和原子读取发生在获取栅栏之前,栅栏同步。 (具体要求见 § 29.8/2。)

这种同步意味着任何发生的事情——在释放栅栏发生之前——在任何事情发生之前——在获取栅栏之后。因此,对a的非原子写入发生在a的非原子读取之前。

当您在循环中编写变量时,事情会变得更加棘手,因为您可能会为某些特定迭代而不是其他迭代建立先发生关系,从而导致数据竞争。

std::atomic<int> f(0);
int a;

void func1()
{
for (int i = 0; i<1000000; ++i) {
a = i;
atomic_thread_fence(std::memory_order_release);
f.store(i, std::memory_order_relaxed);
}
}

void func2()
{
int prev_value = 0;
while (prev_value < 1000000) {
while (true) {
int new_val = f.load(std::memory_order_relaxed);
if (prev_val < new_val) {
prev_val = new_val;
break;
}
}

atomic_thread_fence(std::memory_order_acquire);
std::cout << a << '\n';
}
}

此代码仍会导致栅栏同步,但不会消除数据争用。例如,如果f.load()恰好返回 10,那么我们知道a=1a=2、...a=10都发生在特定的cout<<a之前,但我们没有 知道cout<<a发生在a=11之前。这些是不同线程上的冲突操作,没有发生之前的关系;一场数据竞赛。

关于c++ - 了解 C++11 内存栅栏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13632344/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com