c – 除了它有一个construtor之外,使用std :: atomic和POD结构是否可以?

我正在使用一些原子变量,所有unsigned int,我想将它们收集到一个结构中 – 实际上是一个POD.但是我也想要一个构造函数,因为我的编译器不是很好的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,但我读到的其他问题表明结构需要是可复制的或者其他一些……无论如何,我感到很困惑,我想要要知道我是否可以安全地使用我的struct 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;
最佳答案
其他人说过,但为了清楚起见,我认为你需要这个:

struct my_counters {
    std::atomic<int> counter1;
    std::atomic<int> counter2;
    std::atomic<int> counter3;
    // Constructor so that I can init the values I want.
    my_counters(c1, c2, c3) : counter1(c1), counter2(c2), counter3(c3){;}
};

然后简单地说:

my_counters counters;

换句话说,它是原子的计数器,而不是结构.结构只是用于将它们组合在一起并初始化它们.

由彼得编辑

如果同时使用来自不同线程的这些计数器,则可能希望通过将每个计数器放在单独的高速缓存行中来避免线程之间的错误共享争用. (通常为64字节).您可以在成员上使用C 11 alignas来使编译器填充结构布局,或者在每个原子之间手动插入一些虚拟字符填充[60]成员.

由我编辑

关于理解缓存一般here的良好链接.值得一读.英特尔缓存线现在似乎是64字节,只需要一点点谷歌搜索,但不要引用我.

我的另一个编辑

在下面的评论中已经说了很多关于使用std :: atomic来管理(任意)类或结构的细节,例如:

struct MyStruct
{

    int a;
    int b;
};

std::atomic<MyStruct> foo = { };

但我的问题是:这什么时候有用?具体来说,正如ivaigult指出的那样,你不能使用std :: atomic以线程安全的方式改变MyStruct的各个成员.你只能使用它来加载,存储或交换整个东西,并希望这样做并不常见.

我能想到的唯一合法用例是当你希望能够在线程之间共享类似(例如)struct tm之类的东西时,线程不会在不一致的状态下看到它.然后,如果结构很小,您可能会在没有锁定特定平台的情况下逃脱,这很有用.如果不能,请注意其影响(优先级倒置是最严重的,对于实时代码).

如果你想在线程之间共享一个结构并能够以线程安全的方式更新各个成员,那么std :: atomic不会削减它(也不是它的设计).然后,你必须使用互斥锁,为了做到这一点,从std :: mutex中导出你的结构是很方便的,如下所示:

struct AnotherStruct : public std::mutex
{

    int a;
    int b;
};

现在我可以做(例如):

AnotherStruct bar = { };

bar.lock ().
bar.a++;
bar.b++;
bar.unlock ();

这允许您以线程安全的方式更新两个(可能以某种方式链接)变量.

如果对于那些经验丰富的活动家来说这一切都很明显,我很抱歉,但我想在自己的脑海里澄清一些事情.它实际上与OP的问题无关.

转载注明原文:c – 除了它有一个construtor之外,使用std :: atomic和POD结构是否可以? - 代码日志