PascalABC.NET

Синхронизация и директива critical

Директива critical исключает параллельное выполнение следующего за ней оператора.

{$omp critical имя}
оператор;

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

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

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

var a:integer:=0;
{$omp parallel for}
for var i:integer:=1 to 100 do
  {$omp critical}
  a:=a+1;

Здесь критическую секцию можно использовать вместо редукции. Весь оператор a:=a+1 выполнится одним потоком и только потом – другим. Однако использование критических секций обычно снижает эффективность за счет последовательного выполнения этих участков. В этом примере все тело цикла является критической секцией, поэтому весь цикл будет выполнен последовательно.

Но не во всех случаях использование критических секций помогает обеспечить корректную работу параллельных конструкций.

var a:integer := 0;
{$omp parallel sections}
begin
  {$omp critical}
  a:=1;
  {$omp critical}
  a:=a+1;
end;

Значение переменной a зависит от того, в каком порядке выполнятся секции. Если первая секция выполнится раньше, значение a будет равно двум, иначе – единице.

При использовании критических секций возможно возникновение взаимоблокировок. Например, первый поток выполняет код, содержащий критическую секцию A, внутри которой есть критическая секция B. Второй поток выполняет код, содержащий критическую секцию B, внутри которой есть критическая секция A. Возможен такой порядок выполнения: первый поток входит в секцию А и не успевает войти в секцию В. Второй поток входит в секцию В и не может войти в секцию А, так как эта секция уже выполняется другим потоком. Первый поток не может продолжить выполнение, так как секция В уже выполняется другим потоком. Оба потока оказываются заблокированными.