std :: C Может ли асинхронное чтение и запись в непустой очереди привести к состоянию гонки?

#c #multithreading #mutex #producer-consumer

#c #многопоточность #мьютекс #производитель-потребитель

Вопрос:

Я использую std::queue систему типа pub-sub.

  • Основной поток помещает некоторые данные в очередь и продолжает (здесь нет блокировки мьютекса)
 // no need to acquire any lock as only the main thread inserts in the queue
queue.push(dataToLog);    
// notify the consumer thread waiting on this new available data
conditionalVariable.notify_one();
 
  • Рабочий поток извлекает данные при установке условной переменной
 std::unique_lock<std::mutex> locker(queueMutex);
    
// put the thread to sleep till the thread is not notified in the producer code and queue is empty
conditionalVariable.wait(locker, []() {return !queue.empty();});
    
// get the data once it is available
const data * latestData = queue.front();
queue.pop();
locker.unlock();
 

мой вопрос в том, является ли это потокобезопасным кодированием, учитывая тот факт, что при помещении данных в очередь блокировки нет. Пожалуйста, обратите внимание, что существует проверка conditionalVariable.wait(locker, []() {return !queue.empty();}); , чтобы убедиться, что потребитель ничего не выдает, когда очередь пуста. Но может возникнуть ситуация, когда основной поток записывает, но рабочий поток одновременно завершает работу. Считается ли это условием гонки? Моя путаница связана с тем фактом, что, хотя очередь является общей переменной между ведущим и рабочим потоком, но фактические данные, к которым они обращаются, т.Е. Передняя и задняя части очереди, фактически не являются общими.

Должен ли я размещать блокировки при размещении данных в очереди?

Только один поток записывает в очередь, и только один поток (отличный от того, который записывает) считывает данные из очереди.

Комментарии:

1. Это не имеет ничего общего с очередями и все связано с тем, как работают переменные условия. Вы всегда получаете блокировку для модификации.

Ответ №1:

Да, попытка одновременного выполнения a push и a pop кажется условием гонки.

Независимо от того, являются ли передняя и задняя части очереди фактически общими или нет (в зависимости от того, как они решили это реализовать), в очереди могут легко быть некоторые внутренние переменные, которые изменяются обоими push и pop , поэтому их одновременное выполнение вызывает проблему.

Точная формулировка из стандарта:

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

Если вы не можете найти что-то, чтобы сказать это push , и pop оба являются атомарными (чего я не вижу), вы выполняете две потенциально параллельные конфликтующие операции, ни одна из которых не является атомарной. Поэтому у вас, похоже, неопределенное поведение.