Другие говорили это, но просто для ясности, я думаю, вам нужно это:
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;
Другими словами, это счетчикикоторые являются атомарными, а не структурой.Структура просто служит для их группировки и инициализации.
Edit by Peter
Если вы используете эти счетчики из разных потоков одновременно, вы можете захотетьизбегайте ложного разделения между потоками, помещая каждый счетчик в отдельную строку кэша.(Обычно 64 байта).Вы можете использовать C ++ 11 alignas на элементах, чтобы ваш компилятор дополнял структуру структуры, или вручную вставлять несколько фиктивных char padding[60]
элементов между каждым atomic
.
Редактировать мной
Хорошая ссылка о понимании кэша в целом здесь .Стоит прочтения.Похоже, что в наши дни строки кэша Intel составляют 64 байта из-за быстрого поиска в Google, но не цитируйте меня.
Еще одно редактирование мной
МногоВ комментариях ниже было сказано о плюсах и минусах использования std :: atomic для присмотра (произвольного) класса или структуры, например,
struct MyStruct
{
int a;
int b;
};
std::atomic<MyStruct> foo = { };
Но у меня такой вопрос: когда это когда-либо будет полезно? В частности, как указывает ivaigult, вы не можете использовать std :: atomic для изменения отдельных элементов MyStruct
потокобезопасным способом.Вы можете использовать его только для загрузки, хранения или обмена всего, и желание делать это не так уж часто.
Единственный законный вариант использования, о котором я могу подумать, это когда вы хотите иметь возможность поделиться чем-то вроде(например) struct tm между потоками таким образом, чтобы поток никогда не видел его в несогласованном состоянии.Затем, если структура мала, вы можете обойтись без блокировки на вашей конкретной платформе, и это полезно.Просто помните о последствиях (инверсия приоритетов является наиболее серьезной для кода реального времени), если вы не можете.
Если вы do хотите разделить struct
между потоками и бытьвозможность обновлять отдельные элементы потокобезопасным способом, тогда std::atomic
не обрезает его (и не было предназначено для этого).Затем вам нужно прибегнуть к мьютексу, и для этого удобно извлечь свою структуру из std::mutex
следующим образом:
struct AnotherStruct : public std::mutex
{
int a;
int b;
};
И теперь я могу сделать (например):
AnotherStruct bar = { };
bar.lock ().
bar.a++;
bar.b++;
bar.unlock ();
Это позволяет вам обновлять две (предположительно каким-то образом связанные) переменные потокобезопасным способом.
Извините, если все это очевидно для более опытных участников кампании, ноЯ хотел уточнить вещи в моем собственном уме.На самом деле это не имеет ничего общего с вопросом ОП.