10.3. Критические разделы

На верх  Назад  Вперёд

При программировании потоков иногда необходимо избегать одновременного доступа к некоторым ресурсам, или одновременного вызова подпрограммы из двух потоков. Это может быть сделано с использованием Critical Section (критический раздел). Менеджер кучи FPC использует критические разделы, если включена многопоточность.

Тип TRTLCriticalSection – это непрозрачный (Opaque) тип. Он зависит от операционной системы, на которой выполняется код. Он должен быть инициализирован перед первым использованием, и должен быть освобождён, когда в нём больше нет необходимости.

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

Когда защищённый код завершится, должна быть вызвана LeaveCriticalSection: она включает другие потоки и разрешает их выполнение в защищённом коде. Чтобы минимизировать время ожидания для других потоков, важно создавать и удерживать защищённый блок как можно меньше.

Объявления этих подпрограмм следующие:

procedure InitCriticalSection(var cs: TRTLCriticalSection);
procedure DoneCriticalSection(var cs: TRTLCriticalSection);
procedure EnterCriticalSection(var cs: TRTLCriticalSection);
procedure LeaveCriticalSection(var cs: TRTLCriticalSection);

Смысл этих подпрограмм также очевиден:

InitCriticalSection – инициализирует критический раздел. Это вызов нужно делать перед использованием EnterCrititicalSection или LeaveCriticalSection.

DoneCriticalSection – освобождает ресурсы, связанные с критическим разделом. После этого вызова ни EnterCrititicalSection ни LeaveCriticalSection не могут быть использованы.

EnterCriticalSection – когда этот вызов возвращает результат, вызывающий поток является единственным выполняемым потоком между вызовом EnterCriticalSection и последующим вызовом LeaveCriticalsection.

LeaveCriticalSection – сигнализирует о том, что защищённый код может выполняться другими потоками.

Учтите, что вызов LeaveCriticalsection должен быть выполнен. Если этого не сделать, то все другие потоки не смогут выполнять код в критическом разделе. Поэтому рекомендуется заключать критический раздел в блок Try..finally. Обычно код выглядит следующим образом:

Var
  MyCS : TRTLCriticalSection;
 
Procedure CriticalProc;
begin
  EnterCriticalSection(MyCS);
  Try
  // Защищённый код
  Finally
    LeaveCriticalSection(MyCS);
  end;
end;
 
Procedure ThreadProcedure;
begin
  // Код, выполняемый в потоке…
  CriticalProc;
  // Другой код, выполняемый в других потоках…
end;
 
begin
  InitCriticalSection(MyCS);
// Код запуска потоков.
  DoneCriticalSection(MyCS);
end.