Да, оно того стоит. На самом деле обязательно использовать std::atomic
или синхронизировать доступ к неатомарному, если несколько потоков используют одну и ту же переменную и хотя бы один из них выполняет запись в переменную. Несоблюдение этого правила является неопределенным поведением гонки данных.
В зависимости от того, как вы используете std::size_t
, компилятор может предположить, что неатомарные и иным образом несинхронизированные переменные не будут меняться из других потоков и соответствующим образом оптимизировать код. Это может привести к возникновению Bad Things™.
Мой обычный пример для этого — цикл, в котором используется неатомарное логическое значение:
// 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;
}
}
При компиляции этого кода с включенной оптимизацией GCC и Clang будут проверять keepRunning
только один раз, а затем запускать бесконечный цикл. См. https://godbolt.org/z/GYMiLE для сгенерированного ассемблера.
то есть они оптимизируют его в if (keepRunning) infinite_loop;
, снимая нагрузку с цикла. Поскольку он не является атомарным, им разрешено предполагать, что никакой другой поток не может его писать. См. Многопоточная программа застряла в оптимизированном режиме, но работает нормально в -O0 для более подробного рассмотрения той же проблемы.
Обратите внимание, что этот пример показывает ошибку только в том случае, если тело цикла достаточно простое. Однако неопределенное поведение все еще присутствует, и его следует избегать, используя std::atomic или синхронизацию.
В этом случае вы можете использовать std::atomic<bool>
с std::memory_order_relaxed
, потому что вам не нужна синхронизация или упорядочение. другие операции в потоке записи или чтения. Это даст вам атомарность (без разрывов) и предположение, что значение может изменяться асинхронно, не заставляя компилятор использовать какие-либо инструкции барьера asm для создания большего порядка в отношении. другие операции.
Таким образом, можно и безопасно использовать атомарные объекты без какой-либо синхронизации и даже без создания синхронизации между модулем записи и чтения, как это происходит при загрузке и сохранении seq_cst или получении/выпуске. Вы можете использовать эту синхронизацию для безопасного совместного использования неатомарной переменной или массива, например. с atomic<int*> buffer
, который читатель читает, когда указатель не равен NULL.
Но если совместно используется только сама атомарная переменная, вы можете просто попросить читателей прочитать текущее значение, не заботясь о синхронизации. Вы можете захотеть прочитать его в локальном временном файле, если вам не нужно перечитывать каждую итерацию короткого цикла, только один раз за вызов функции.
person
Finn
schedule
18.01.2020