Что может произойти, если два потока одновременно обращаются к одной и той же переменной bool? - PullRequest
5 голосов
/ 07 декабря 2011

У меня есть кроссплатформенная программа на 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 ]

5 голосов
/ 07 декабря 2011

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

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

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

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

1 голос
/ 07 декабря 2011

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

0 голосов
/ 07 декабря 2011

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

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

...