从 C++ 中的另一个线程读取指针
Reading pointers from another thread in C++
在下面的代码中,线程 2 中 x
的值将始终为 10,因为原子线程栅栏。
int x;
atomic<bool> b(false);
// thread 1:
x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(x == 10); // x will always be 10
但是在下面的代码中,线程2中的*x
是否总是10?
int* x = new int;
atomic<bool> b(false);
// thread 1:
*x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(*x == 10); // will *x always be 10?
在这两种情况下你都得到10
,这里存储是直接完成还是通过指针完成没有区别。
这里不需要内存栅栏,因为 b = true
本质上是 b.store(true, std::memory_order_seq_cst)
- 带栅栏的获取-释放。
这样的内存顺序可防止编译器围绕操作对存储和加载进行重新排序,并使前面的存储在该存储可见时对其他线程可见。
如果比较这两个函数生成的代码:
#include <atomic>
int x;
std::atomic<bool> b(false);
void f() {
x = 10;
std::atomic_thread_fence(std::memory_order_release);
b = true;
}
void g() {
x = 10;
b = true;
}
完全相同:
f():
movl , x(%rip)
movb , b(%rip)
mfence
ret
g():
movl , x(%rip)
movb , b(%rip)
mfence
ret
但是在你的特殊情况下,在我看来你只需要 std::memory_order_release
存储到 b
就可以使其他线程也可以看到 x
的存储,栅栏是不必要的。 IE。 b.store(true, std::memory_order_release)
这里就够了。消费者代码需要做 b.load(std::memory_order_acquire)
.
标准互斥锁在锁定时获取内存顺序并在解锁时释放内存顺序(这是术语 acquire/release 的来源),不涉及栅栏。
很少需要明确的围栏,主要是在硬件驱动程序中。由于对 C++11 内存模型的误解,通常会在 user-space 模式下设置代码栅栏。栅栏是最昂贵的原子同步机制,这是避免它们的主要原因。
在下面的代码中,线程 2 中 x
的值将始终为 10,因为原子线程栅栏。
int x;
atomic<bool> b(false);
// thread 1:
x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(x == 10); // x will always be 10
但是在下面的代码中,线程2中的*x
是否总是10?
int* x = new int;
atomic<bool> b(false);
// thread 1:
*x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(*x == 10); // will *x always be 10?
在这两种情况下你都得到10
,这里存储是直接完成还是通过指针完成没有区别。
这里不需要内存栅栏,因为 b = true
本质上是 b.store(true, std::memory_order_seq_cst)
- 带栅栏的获取-释放。
这样的内存顺序可防止编译器围绕操作对存储和加载进行重新排序,并使前面的存储在该存储可见时对其他线程可见。
如果比较这两个函数生成的代码:
#include <atomic>
int x;
std::atomic<bool> b(false);
void f() {
x = 10;
std::atomic_thread_fence(std::memory_order_release);
b = true;
}
void g() {
x = 10;
b = true;
}
完全相同:
f():
movl , x(%rip)
movb , b(%rip)
mfence
ret
g():
movl , x(%rip)
movb , b(%rip)
mfence
ret
但是在你的特殊情况下,在我看来你只需要 std::memory_order_release
存储到 b
就可以使其他线程也可以看到 x
的存储,栅栏是不必要的。 IE。 b.store(true, std::memory_order_release)
这里就够了。消费者代码需要做 b.load(std::memory_order_acquire)
.
标准互斥锁在锁定时获取内存顺序并在解锁时释放内存顺序(这是术语 acquire/release 的来源),不涉及栅栏。
很少需要明确的围栏,主要是在硬件驱动程序中。由于对 C++11 内存模型的误解,通常会在 user-space 模式下设置代码栅栏。栅栏是最昂贵的原子同步机制,这是避免它们的主要原因。