Что может произойти, если два потока одновременно обращаются к одной и той же переменной bool?

У меня есть кроссплатформенная программа C++, где я использую библиотеки повышения для создания асинхронного таймера.
У меня есть глобальная переменная:

bool receivedInput = false;

Один поток ожидает и обрабатывает ввод

string argStr;
while (1) 
{
     getline(cin, argStr);
     processArguments(argStr);
     receivedInput = true;
}

Другой поток запускает таймер, где обратный вызов вызывается каждые 10 секунд. В этом обратном вызове я проверяю, получило ли я сообщение

if (receivedInput)
{
    //set up timer to fire again in 10 seconds
    receivedInput = false;
}
else
    exit(1);

Так это безопасно? Для чтения в потоке 2, я думаю, это не имеет значения, так как условие оценивается как true или false. Но я не уверен, что произойдет, если оба потока попытаются установить receiveInput одновременно. Я также увеличил время таймера в 3 раза по сравнению с периодом ожидания ввода, поэтому я не беспокоюсь о состоянии гонки.

Редактировать: Чтобы решить эту проблему, я использовал boost::unique_lock, когда я установил receiveInput и boost::shared_lock, когда я прочитал receiveInput. Я использовал пример отсюда

3 ответа

Решение

Это принципиально небезопасно. После того, как поток 1 написал true в receivedInput не гарантируется, что поток 2 увидит новое значение. Например, компилятор может оптимизировать ваш код, делая определенные предположения о значении receivedInput в то время как оно используется в качестве условия if или кэширует его в регистре, поэтому вы не гарантируете, что основная память будет фактически прочитана во время выполнения условия if. Кроме того, и компилятор, и процессор могут изменять порядок чтения и записи для оптимизации, например true может быть написано receivedInput до getLine() а также processArguments(),

Более того, полагаться на синхронизацию для синхронизации - очень плохая идея, поскольку часто у вас нет никаких гарантий относительно количества процессорного времени, которое каждый поток получит в заданном интервале времени, или вообще будет ли оно запланировано в заданном интервале времени.

Распространенная ошибка - думать, что receivedInputvolatile может помочь здесь По факту, volatile гарантирует, что значения на самом деле считываются / записываются в основную память (вместо того, чтобы, например, кэшироваться в регистре) и что операции чтения и записи переменной упорядочены относительно друг друга. Тем не менее, это не гарантирует, что чтение и запись volatile Переменные упорядочены относительно других инструкций.

Вам нужны барьеры памяти или надлежащий механизм синхронизации, чтобы это работало так, как вы ожидаете.

Вы должны были бы проверить свой стандарт нити. Предполагая, что мы говорим о потоках POSIX, это явно неопределенное поведение - объект не может быть доступен одному потоку, в то время как другой поток изменяет или может изменять его. Все может случиться.

Если ваши потоки используют значение receiveInput для управления независимыми блоками кода, но не для синхронизации друг с другом, существует одно простое решение:

добавьте "volatile" перед receiveInput, чтобы компилятор не выполнял оптимизацию, мешая потокам разделять значение receiveInput.

Другие вопросы по тегам