- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我正在尝试理解 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=1
、a=2
、...a=10
都发生在特定的cout<<a
之前,但我们没有 知道cout<<a
发生在a=11
之前。这些是不同线程上的冲突操作,没有发生之前的关系;一场数据竞赛。
关于c++ - 了解 C++11 内存栅栏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13632344/
我有一个栅栏的 3D 模型。该模型包含 1 个起始柱和 1 个末端柱,中间有一个连接玻璃板。假设我希望能够指定任意长度的栅栏,并将其放置在虚拟世界中。如果可能的话,我将如何处理原始 3D 模型以将其更
x86指令lfence/sfence/mfence用于实现Linux内核中的rmb()/wmb()/mb()机制。很容易理解,这些用于序列化内存访问。但是,在遇到运行时行为中的错误之前,在编写代码时确
以下代码实现了一些无锁(且无原子!)的线程间通信,这些通信需要使用存储和加载内存屏障,但是C++ 11 release-acquire语义不适当,也不保证正确性。实际上,该算法暴露了对发布获取语义的某
我是一名优秀的程序员,十分优秀!