我想在if条件下更新两个原子变量,if条件使用其中一个原子变量。我不确定这两个原子变量是否会一起更新。
下面是一个多线程代码。在“if(local〉a1)”中,a1是一个原子变量,因此阅读if条件是否是跨线程的原子变量。换句话说,如果线程t1处于if条件,线程t2是否会等待线程t1更新a1?是否有可能一个线程更新a2,而另一个线程更新a1?
// constructing atomics
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
std::atomic<int> a1{0};
std::atomic<int> a2{0};
void count1m (int id) {
double local = id;
double local2 = id*3;
*if(local > a1) {* // a1 is an atomic variable so will reading it in if condition be atomic across threads or not?
a1 = local;
a2 = local2;
}
};
int main ()
{
std::vector<std::thread> threads;
std::cout << "spawning 20 threads that count to 1 million...\n";
for (int i=20; i>=0; --i) {
threads.push_back(std::thread(count1m,i));
}
for (auto& th : threads) th.join();
cout << "a1 = " << a1 << endl;
}
2条答案
按热度按时间icnyk63a1#
我不确定这两个原子变量是否会一起更新。
不是
原子意味着 * 不可分割 *,因为对原子的写入不能被读到一半,处于中间或不完整的状态。
然而,对一个原子的更新并不与对另一个原子的更新一起批处理,编译器如何知道 * 哪些 * 更新应该像这样批处理呢?
如果你有两个原子变量,你就有两个独立的对象,它们都不能被单独观察到部分写入状态,你仍然可以同时读取它们,并看到另一个线程更新了一个而没有更新另一个的状态,即使代码中的存储是相邻的。
可能性包括:
1.使用互斥锁即可。
你在评论中排除了这一点,但为了完整起见,我还是要提到它,因为这是迄今为止最简单的方法。
1.将两个对象打包到一个原子中。
注意,如果你的平台没有原生的128位原子,一个128位对象(足够大,可以容纳两个binary 64 double)可能需要在内部使用互斥锁或类似的同步原语,你可以检查
std::atomic<DoublePair>::is_lock_free()
来找出答案(对于一个合适的包含一对double的结构体DoublePair
)。在互斥禁止下,非无锁原子是否可接受,我无法猜测。
DoublePair
对象的循环数组中,并原子地更新 * 那个 *(对于多个生产者,有各种各样的方案,但单个生产者肯定更简单--不要忘记A-B-A保护)主要的问题是你说你不允许使用互斥锁,但是你没有说“为什么”。代码必须是无锁的吗?无等待的吗?有人真的讨厌
std::mutex
,但是会接受任何其他的同步原语吗?wribegjk2#
基本上有两种方法可以做到这一点,它们是不同的。
第一种方法是创建一个可以立即更新的原子结构。注意,使用这种方法时,存在争用条件,在
aip
更新之前,local
和aip.a1
之间的比较可能会发生变化。第二种方法是使用互斥锁来同步整个节,如下所示。这将保证不会发生竞态条件,并且所有操作都是原子完成的。我们使用了std::lock_guard来提高安全性,而不是手动调用
m.lock()
和m.unlock()
。