std::atomic<T>::notify_all 是如何排序的?
How is std::atomic<T>::notify_all ordered?
我希望下面的程序不会挂起。
如果在(1)中观察到(2)和(3)的顺序相反,可能会因为丢失通知而挂起:
#include <atomic>
#include <chrono>
#include <thread>
int main()
{
std::atomic<bool> go{ false };
std::thread thd([&go] {
go.wait(false, std::memory_order_relaxed); // (1)
});
std::this_thread::sleep_for(std::chrono::milliseconds(400));
go.store(true, std::memory_order_relaxed); // (2)
go.notify_all(); // (3)
thd.join();
return 0;
}
所以问题是这里会发生什么:
- 程序可能会挂起,我必须使用栅栏来防止它。围栏到底是什么,在哪里以及为什么?
- 程序可能没有挂起。那么如何防止提到的重新排序?我不是在问实现,我是在问标准措辞。
标准规定:
A call to an atomic waiting operation on an atomic object M is eligible to be unblocked by a call to an atomic notifying operation on M if there exist side effects X and Y on M such that:
- the atomic waiting operation has blocked after observing the result of X,
- X precedes Y in the modification order of M, and
- Y happens before the call to the atomic notifying operation.
关于nofify_all
:
Effects: Unblocks the execution of all atomic waiting operations on *ptr
that are eligible to be unblocked by this call.
在您的示例中,go
(M) 的初始化对应于 X,而 store
(2) 到Y。初始化发生在等待调用之前,存储发生在通知调用之前。 store happens-before the notify 因为它先于它排序,并且两个函数都对同一个对象进行操作。存储本身放宽并不重要,因为内存顺序只对 surrounding 操作进行排序。 [intro.races] 6.9.2.1.19 状态:
The [..] coherence requirements effectively disallow compiler reordering of atomic operations to a single object, even if both operations are relaxed loads. This effectively makes the cache coherence guarantee provided by most hardware available to C++ atomic operations.
不幸的是,标准在可见性方面相当模糊,但据我了解,被某些通知调用解除阻塞的等待调用保证观察到通知调用之前发生的最新变化(或一些后来的值) - 就像条件变量的情况一样
所以不,你的程序不能挂起。
我希望下面的程序不会挂起。
如果在(1)中观察到(2)和(3)的顺序相反,可能会因为丢失通知而挂起:
#include <atomic>
#include <chrono>
#include <thread>
int main()
{
std::atomic<bool> go{ false };
std::thread thd([&go] {
go.wait(false, std::memory_order_relaxed); // (1)
});
std::this_thread::sleep_for(std::chrono::milliseconds(400));
go.store(true, std::memory_order_relaxed); // (2)
go.notify_all(); // (3)
thd.join();
return 0;
}
所以问题是这里会发生什么:
- 程序可能会挂起,我必须使用栅栏来防止它。围栏到底是什么,在哪里以及为什么?
- 程序可能没有挂起。那么如何防止提到的重新排序?我不是在问实现,我是在问标准措辞。
标准规定:
A call to an atomic waiting operation on an atomic object M is eligible to be unblocked by a call to an atomic notifying operation on M if there exist side effects X and Y on M such that:
- the atomic waiting operation has blocked after observing the result of X,
- X precedes Y in the modification order of M, and
- Y happens before the call to the atomic notifying operation.
关于nofify_all
:
Effects: Unblocks the execution of all atomic waiting operations on
*ptr
that are eligible to be unblocked by this call.
在您的示例中,go
(M) 的初始化对应于 X,而 store
(2) 到Y。初始化发生在等待调用之前,存储发生在通知调用之前。 store happens-before the notify 因为它先于它排序,并且两个函数都对同一个对象进行操作。存储本身放宽并不重要,因为内存顺序只对 surrounding 操作进行排序。 [intro.races] 6.9.2.1.19 状态:
The [..] coherence requirements effectively disallow compiler reordering of atomic operations to a single object, even if both operations are relaxed loads. This effectively makes the cache coherence guarantee provided by most hardware available to C++ atomic operations.
不幸的是,标准在可见性方面相当模糊,但据我了解,被某些通知调用解除阻塞的等待调用保证观察到通知调用之前发生的最新变化(或一些后来的值) - 就像条件变量的情况一样
所以不,你的程序不能挂起。