Да, оно того стоит. Фактически обязательно использовать std::atomic
или синхронизировать доступ к неатоми c, если несколько потоков используют одну и ту же переменную и по крайней мере один из них выполняет запись в переменную. Несоблюдение этого правила ведет к неопределенному поведению гонки данных.
В зависимости от того, как вы используете std::size_t
, компилятор может предположить, что неатомные c и другие несинхронизированные переменные не будут отличаться от других потоков и оптимизировать код соответственно. Это может привести к возникновению Bad Things ™.
Мой обычный пример для этого - al oop, где используется неатоми c boolean:
// make keepRunning an std::atomic<bool> to avoid endless loop
bool keepRunning {true};
unsigned number = 0;
void stop()
{
keepRunning = false;
}
void loop()
{
while(keepRunning) {
number += 1;
}
}
При компиляции этого кода при включенных оптимизациях G CC и Clang будут проверять только keepRunning
один раз и затем запускать бесконечный l oop. См. https://godbolt.org/z/GYMiLE для генерируемого вывода ассемблера.
т.е. они оптимизируют его в if (keepRunning) infinite_loop;
, поднимая нагрузку из l oop. Поскольку это не атомы c, им разрешается предполагать, что никакой другой поток не может их записать. См. Программа многопоточности, застрявшая в оптимизированном режиме, но обычно работающая в -O0 для более подробного рассмотрения этой проблемы.
Обратите внимание, что этот пример показывает ошибку только, если тело l oop достаточно прост. Однако неопределенное поведение все еще присутствует, и его следует избегать с помощью std :: atomi c или синхронизации.
В этом случае вы можете использовать std::atomic<bool>
с std::memory_order_relaxed
, потому что вам не нужна синхронизация или упорядочение по сравнению с. другие операции в потоке записи или чтения. Это даст вам атомарность (без разрывов) и допущение, что значение может изменяться асинхронно, не заставляя компилятор использовать какие-либо инструкции asm-барьера для создания большего порядка порядка. другие операции.
Таким образом, можно и безопасно использовать атомику без какой-либо синхронизации и даже без создания синхронизации между записывающим устройством и считывателем, как это делает seq_cst или получение / освобождение загрузки и сохранения. Вы можете использовать эту синхронизацию для безопасного совместного использования переменной или массива не-Atomi c, например, с atomic<int*> buffer
, который считывает читатель, когда указатель отличен от NULL.
Но если только атоми c Сама переменная является общей, вы можете просто заставить читателей читать текущее значение, не заботясь о синхронизации. Возможно, вы захотите прочитать его в локальном временном файле, если вам не нужно перечитывать каждую итерацию короткого l oop, только один раз за вызов функции.