Могут ли два процесса одновременно войти в состояние активного ожидания по алгоритму Петерсона?

Вопрос или проблема

Посмотрев на алгоритм Питерсона, эквивалентная реализация на C++ выглядит так:

#include <thread>
#include <atomic>
int main(){
  std::atomic<bool> flag[2] = {false,false};
  std::atomic<int> turn = {0};
  std::thread t1([&](){
     flag[0] = true;  // flag_set_0
     turn = 1;    // turn_set_0
     while(flag[1] && turn == 1){  // flag_read_0, turn_read_0
         //busy wait
     }
     // критическая секция
     flag[0] = false;
  });
  std::thread t2([&](){
     flag[1] = true;  // flag_set_1
     turn = 0;  // turn_set_1
     while(flag[0] && turn == 0){ // flag_read_1, turn_read_1
         //busy wait
     }
     // критическая секция
     flag[1] = false;
  });
  t1.join();
  t2.join();
}

В этой программе, если порядок выполнения таков:

flag[0] = true
turn = 1
flag[1] = true
while(flag[1] && turn == 1){} // true
turn = 0
while(flag[0] && turn == 0){} //true

Единственный полный порядок это
flag_set_0 < turn_set_0 < flag_set_1 < flag_read_0 < turn_read_0 < turn_set_1 < flag_read_1 < turn_read_1,

что является допустимым единым полным порядком без противоречий, поскольку он соблюдает ограничения, требуемые t1 и t2:

t1: flag_set_0 < turn_set_0 < flag_read_0 < turn_read_0
t2: flag_set_1 < turn_set_1 < flag_read_1 < turn_read_1

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

Да, они могут оба войти. Но один из них вошел, когда turn был другим, перед последним записью в него. На следующей итерации его условие цикла будет ложным. Это не взаимная блокировка.

Циклы ожидания проверяют как flag[other_thread], так и turn на каждой итерации, с противоположными условиями для turn, которые не могут быть истинными для обоих циклов бесконечно.

Да, я понимаю ваш смысл, единственная возможность заключается в том, что реализация не подчиняется [атомарные. порядок] p11 (должна в разумные сроки), так что она всегда видит, что turn соответствует условию, даже если другой поток изменил его. Но это не вина алгоритма, это вина реализаций, верно? В этой ситуации все операции в условии предшествовали изменению turn в едином полном порядке S, верно?

Это операции seq_cst, так что я не уверен, возможно ли даже задержанное видение после того, как оба цикла вошли в свои циклы ожидания. Это предполагает, что они оба прочитали turn после его записи.

Возможно, это просто деталь реализации, что реальное оборудование должно подождать, пока seq_cst запись станет глобально видимой, прежде чем seq_cst загрузка сможет перезагрузить её (или фактически все предыдущие seq_cst записи станут видимыми). Но если нет, то последняя присваивание turn= уже видимо для другого потока к тому времени, когда второй поток входит в свой цикл ожидания. Возможно, что никакие требования seq_cst не будут нарушены, если оба потока читают противоположные значения turn на протяжении нескольких итераций в этом случае; если так, то да, только требования по «должны» видимости предотвратят взаимную блокировку.


Если вы считаете, что хорошо известный алгоритм, который известен десятилетиями, неисправен, весьма вероятно, что вы ошиблись и должны продолжать искать причины, почему он не неисправен. (Или если вам нужно попросить о помощи в его понимании, по крайней мере, сформулируйте свой вопрос с предположением, что алгоритм правильный. Например: «Как алгоритм Питерсона избегает взаимной блокировки, когда оба потока входят в циклы ожидания?» Формулировка таким образом может побудить вас взглянуть на условия цикла еще раз, или нет.)

Ответ или решение

Могут ли два процесса одновременно войти в состояние ожидания в алгоритме Петерсона?

Алгоритм Петерсона представляет собой классический метод синхронизации процессов, разработанный для решения проблемы взаимного исключения. Основное его предназначение — предотвратить одновременный доступ нескольких процессов к критической секции, что может привести к состояниям гонки и другим проблемам.

Принцип работы алгоритма

Алгоритм реализуется с использованием двух основных переменных: flag и turn. Массив flag[i] указывает, намерен ли процесс i войти в критическую секцию, а переменная turn определяет, какой процесс имеет право войти в критическую секцию в данный момент.

Возможность одновременного входа в busy-wait

Ваш вопрос касается того, могут ли оба процесса одновременно войти в busy wait (ожидание в бесконечном цикле). Рассмотрим сценарий, при котором оба потока выполняют свои команды почти одновременно:

  1. Процесс t1 устанавливает flag[0] = true и turn = 1.
  2. Процесс t2 устанавливает flag[1] = true и turn = 0.

На этом этапе оба процесса читают флаги и значение turn. В случае, если операции записи (flag и turn) не успевают отразиться в памяти для обоих потоков из-за особенностей реализации или архитектуры, оба процесса могут войти в свой цикл ожидания:

  • t1 ожидает, пока flag[1] равно true и turn равно 1.
  • t2 ожидает, пока flag[0] равно true и turn равно 0.

Если предположить некорректную работу с семантикой последовательной согласованности (seq_cst), оба потока могут находиться в состоянии ожидания, что потенциально приведет к ситуации, близкой к взаимной блокировке (deadlock). Однако важно отметить, что сами процессы не находились бы в бесконечном ожидании, поскольку – согласно алгоритму – в какой-то момент один из потоков изменит состояние turn, и условия выхода из ожидания перестанут выполняться.

Аргументация и выводы

  1. Отсутствие взаимной блокировки. Алгоритм обеспечивает, чтобы оба потока не могли бесконечно удерживать взаимную блокировку. Если бы оба потока действительно входили в состояние busy-wait без возможности выхода, то это бы означало наличие ошибки в реализации самого алгоритма, а не в его логике.

  2. Семантика видимости. Как вы правильно заметили, если операции являются последовательными (seq_cst), то после выполнения записи в переменную turn, другой поток должен иметь возможность увидеть это изменение в порядке, установленном системой. Это значит, что принципиально возможно избежать ситуации, в которой два потока видят противоречивые значения turn.

  3. Имплементация. Если реализация исполняемой среды не обеспечивает своевременную видимость изменений переменных между потоками, это создает ограничения, не зависящие от самой логики алгоритма. В стандартных реализациях C++ с использованием <atomic> такие случаи крайне маловероятны.

Заключение

Ответ на ваш вопрос — да, два процесса могут одновременно войти в состояние busy wait, однако это не приводит к взаимной блокировке, благодаря механизму условного ожидания в самом алгоритме. Если правильно следовать логике алгоритма Петерсона и соблюдать условия видимости данных, взаимное исключение гарантируется. Уважение к принципам синхронизации и понимание работы с потоками обеспечивает корректное взаимодействие процессов, позволяя эффективно использовать алгоритм Петерсона в реальных приложениях.

Оцените материал
Добавить комментарий

Капча загружается...