std::memory_order_acq_rel 对其他线程读取的非原子变量的影响
Effect of std::memory_order_acq_rel on non-atomic variable read in other thread
我想我主要理解 C++ 原子库中各种 memory_order
标志的语义。
但是,我对以下情况感到困惑:
假设我们有两个线程 - 线程 A,它是一个 "main execution" 线程,以及线程 B,它是某个任意线程,它是可以调度任务的线程池的一部分,并且 运行.
如果我使用 std::memory_order_acq_rel
执行 "read-write-update" 原子操作,然后对布尔变量执行 非原子 写入,是 non-atomic write 立即对其他线程可见?我认为答案是否定的,除非其他线程也访问执行 "read-write-update" 操作的 atomic 变量。
因此,例如,给定一个全局 std::atomic_flag
变量 X
,一个全局 bool
值 B,以及一个具有成员函数的线程池对象 THREADPOOL
dispatch
,它将在另一个线程中执行任意函数处理程序:
if (!X.test_and_set(std::memory_order_acq_rel)
{
if (SOME_CONDITION) B = true;
THREADPOOL.dispatch([]() {
// This executes in Thread B
if (B) { /* do something */ } // are we guaranteed to see changes to B?
});
}
所以在这个例子中,lambda 函数中的代码将在不同的线程中执行。该线程是否一定会看到第一个线程中对 B
的(非原子)更新?请注意,第二个线程 not 访问 atomic_flag,所以我的理解是 B
的更改必然 not在第二个线程中可以看到。
我的理解对吗?如果是这样,使用 std::memory_order_seq_cst
会改变它吗?
没有内存顺序规范使未来的内存访问可见。至多,它们阻止它们在原子访问可见之前变得可见。
如果要确保特定访问确实可见,则必须对该访问强制执行特定的内存排序,或者您必须有一个使用内存排序的未来访问以确保它在您想要的访问之后排序可见。
所有原子操作都是原子的。内存排序只允许你做三件事:
建立此原子操作相对于先前操作的顺序,无论是否为原子——此操作保证在它们之后。
建立此操作相对于未来操作的顺序,无论是否为原子操作——保证此操作先于它们。
用其他原子操作建立顺序。
None 确保未来的非原子操作发生 "soon" 或在任何特定时间变得可见。
THREADPOOL
中 dispatch
方法的正确实现应该提供 happens-before
在此方法调用之前由 调用者 执行的所有操作之间的关系函数执行的所有操作(在您的情况下为 lambda),传递给方法。
所以,辅助线程,执行你的 lambda 函数,肯定会看到主线程分配的 B
的值。
没有 happens-before 顺序,保证变量修改立即可见性的唯一方法是对修改和读取都使用std::memory_order_seq_cst
。参见,例如,.
我想我主要理解 C++ 原子库中各种 memory_order
标志的语义。
但是,我对以下情况感到困惑:
假设我们有两个线程 - 线程 A,它是一个 "main execution" 线程,以及线程 B,它是某个任意线程,它是可以调度任务的线程池的一部分,并且 运行.
如果我使用 std::memory_order_acq_rel
执行 "read-write-update" 原子操作,然后对布尔变量执行 非原子 写入,是 non-atomic write 立即对其他线程可见?我认为答案是否定的,除非其他线程也访问执行 "read-write-update" 操作的 atomic 变量。
因此,例如,给定一个全局 std::atomic_flag
变量 X
,一个全局 bool
值 B,以及一个具有成员函数的线程池对象 THREADPOOL
dispatch
,它将在另一个线程中执行任意函数处理程序:
if (!X.test_and_set(std::memory_order_acq_rel)
{
if (SOME_CONDITION) B = true;
THREADPOOL.dispatch([]() {
// This executes in Thread B
if (B) { /* do something */ } // are we guaranteed to see changes to B?
});
}
所以在这个例子中,lambda 函数中的代码将在不同的线程中执行。该线程是否一定会看到第一个线程中对 B
的(非原子)更新?请注意,第二个线程 not 访问 atomic_flag,所以我的理解是 B
的更改必然 not在第二个线程中可以看到。
我的理解对吗?如果是这样,使用 std::memory_order_seq_cst
会改变它吗?
没有内存顺序规范使未来的内存访问可见。至多,它们阻止它们在原子访问可见之前变得可见。
如果要确保特定访问确实可见,则必须对该访问强制执行特定的内存排序,或者您必须有一个使用内存排序的未来访问以确保它在您想要的访问之后排序可见。
所有原子操作都是原子的。内存排序只允许你做三件事:
建立此原子操作相对于先前操作的顺序,无论是否为原子——此操作保证在它们之后。
建立此操作相对于未来操作的顺序,无论是否为原子操作——保证此操作先于它们。
用其他原子操作建立顺序。
None 确保未来的非原子操作发生 "soon" 或在任何特定时间变得可见。
THREADPOOL
中 dispatch
方法的正确实现应该提供 happens-before
在此方法调用之前由 调用者 执行的所有操作之间的关系函数执行的所有操作(在您的情况下为 lambda),传递给方法。
所以,辅助线程,执行你的 lambda 函数,肯定会看到主线程分配的 B
的值。
没有 happens-before 顺序,保证变量修改立即可见性的唯一方法是对修改和读取都使用std::memory_order_seq_cst
。参见,例如,