【发布时间】:2018-11-09 03:13:30
【问题描述】:
我正在使用一些原子变量,都是无符号整数,我想将它们收集到一个结构中 - 实际上是一个 POD。但是我也想要一个构造函数,因为我的编译器不是 c++11(所以我必须定义自己的构造函数来使用初始值创建它)。
原来我有:
// Names are not the real names - this is just for example
std::atomic<int> counter1;
std::atomic<int> counter2;
std::atomic<int> counter3;
然后我很高兴根据需要增加/减少它们。但后来我决定我想要更多的计数器,因此将它们放入一个结构中:
struct my_counters {
int counter1;
int counter2;
int counter3;
// Constructor so that I can init the values I want.
my_counters(c1, c2, c3) : counter1(c1), counter2(c2), counter3(c3){;}
};
但由于我添加了自定义构造函数,这在技术上不再是 POD。我正在阅读有关此的其他问题,他们说要使用 std::atomic 我需要一个 POD,但我阅读的其他问题表明该结构需要是可复制的或类似的......无论如何,我很困惑,我想要知道我是否可以安全地将我的结构 my_counters 用作原子类型:
std::atomic<my_counters> counters;
然后在各个线程中:
// Are these operations now still atomic (and therefore safe to use across threads):
counters.counter1++;
counters.counter2--;
counters.counter3 += 4;
【问题讨论】:
-
原子结构与具有原子成员的结构不同。使用原子结构,您必须为每个修改复制 整个 结构。
-
atomic<my_counters>没有.counter1成员,所以counters.counter1++;不会编译。您可以使用cmpxchg循环来实现所有 3 项修改,但 3-int结构仅在少数平台上是无锁的(例如一些带有lock cmpxchg16b的 x86-64 编译器) -
如果你有 3 个独立的原子对象,如果你想从不同的线程中使用它们,不要把它们都放在同一个结构中。将它们全部放在一个缓存行中会导致错误共享,因此使用
counter1的线程将与使用counter2的线程竞争。 (如果它们通常同时使用,那么在同一个缓存行中是好的。) -
std::atomic<T>的主模板要求是T是TriviallyCopyable,而不是POD。my_counters是 TriviallyCopyable -
@code_fodder 所有的 POD 都是 TriviallyCopyable,当他们的意思是“对 memcpy 安全”时,人们可能会说 POD