gpt4 book ai didi

c++ - 为什么这个 cppreference 摘录似乎错误地暗示原子可以保护关键部分?

转载 作者:行者123 更新时间:2023-12-03 23:01:20 24 4
gpt4 key购买 nike

int main() {
std::vector<int> foo;
std::atomic<int> bar{0};
std::mutex mx;
auto job = [&] {
int asdf = bar.load();
// std::lock_guard lg(mx);
foo.emplace_back(1);
bar.store(foo.size());
};
std::thread t1(job);
std::thread t2(job);
t1.join();
t2.join();
}
这显然不能保证工作,但可以使用互斥锁。但是如何用标准的正式定义来解释呢?
考虑一下来自 cppreference 的这段摘录:

If an atomic store in thread A is tagged memory_order_release and anatomic load in thread B from the same variable is taggedmemory_order_acquire [as is the case with default atomics], all memory writes (non-atomic and relaxedatomic) that happened-before the atomic store from the point of viewof thread A, become visible side-effects in thread B. That is, oncethe atomic load is completed, thread B is guaranteed to see everythingthread A wrote to memory.


原子加载和存储(使用默认值或指定的特定获取和释放内存顺序)具有提到的获取-释放语义。 (互斥锁的锁定和解锁也是如此。)
对该措辞的一种解释可能是,当线程 2 的加载操作与线程 1 的存储操作同步时,可以保证观察到发生在存储之前的所有(甚至是非原子的)写入,例如 vector 修改,使得这定义明确。但是几乎每个人都会同意这会导致段错误,并且如果作业功能在循环中运行其三行,肯定会这样做。
什么标准措辞解释了两种工具之间明显的能力差异,因为这种措辞似乎暗示 atomic 会以某种方式同步。
我知道什么时候使用互斥体和原子,并且我知道这个例子不起作用,因为实际上没有同步发生。我的问题是如何解释定义,使其与现实中的工作方式不矛盾。

最佳答案

引用的这段话的意思是,当 B 加载 A 存储的值时,那么通过观察存储发生了,也可以确保 B 在存储之前所做的一切也发生了并且是可见的。
但是,如果商店实际上还没有发生,这并不能告诉您任何事情!
实际的 C++ 标准更明确地说明了这一点。 (永远记住,cppreference 虽然是一种经常引用或解释标准的宝贵资源,但它不是标准本身,也不具有权威性。)来自 N4861 ,最终的 C++20 草案,我们在 atomics.order p2 中有:

An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomicoperation B that performs an acquire operation on M and takes its value from any side effect in the releasesequence headed by A.


我同意,如果您的线程 B 中的负载返回 1,则可以安全地得出另一个线程已完成其存储并因此已退出临界区的结论,因此 B 可以安全地使用 foo .在这种情况下,B 中的负载与 A 中的存储同步,因为负载的值(即 1)来自存储(这是其自己的释放序列的一部分)。
但是完全有可能两个加载都返回 0,如果两个线程在其中一个执行存储之前完成加载。值 0 不是来自任一商店,因​​此在这种情况下负载不会与商店同步。您的代码甚至不查看加载的值,因此在这种情况下,两个线程可能会一起进入临界区。
以下代码是一种安全但效率低下的使用原子来保护临界区的方法。它确保 A 将首先执行临界区,而 B 将等到 A 完成后再继续。 (显然,如果两个线程都在等待另一个线程,那么您就会陷入死锁。)
int main() {
std::vector<int> foo;
std::atomic<int> bar{0};
std::mutex mx;
auto jobA = [&] {
foo.emplace_back(1);
bar.store(foo.size());
};
auto jobB = [&] {
while (bar.load() == 0) /* spin */ ;
foo.emplace_back(1);
};

std::thread t1(jobA);
std::thread t2(jobB);
t1.join();
t2.join();
}

关于c++ - 为什么这个 cppreference 摘录似乎错误地暗示原子可以保护关键部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65499906/

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