Глубины Indy


Критические секции (Critical Sections)


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

Для входа в критическую секцию вызовите метод Enter и затем метод Leave для выхода из секции. Класс TCriticalSection также имеет методы Acquire и Release, которые аналогичны Enter и Leave соответственно.

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

var

  GLogCS: TCriticalSection;

  GUserLog: TStringList;

procedure TformMain.IdTCPServer1Connect(AThread: TIdPeerThread);

var

  s: string;

begin

  // Username

  s := ReadLn;

  GLogCS.Enter;



  try

    GUserLog.Add('User logged in: ' + s);

  finally

    GLogCS.Leave;

  end;

end;

procedure TformMain.Timer1Timer(Sender: TObject);

begin

  GLogCS.Enter;

  try

    listbox1.Items.AddStrings(GUserLog);

    GUserLog.Clear;

  finally

    GLogCS.Leave;

  end;

end;

initialization

  GLogCS := TCriticalSection.Create;

  GUserLog := TStringList.Create;

finalization

  FreeAndNil(GUserLog);

  FreeAndNil(GLogCS);

end.

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

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

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

Примечание пользователям Delphi 4: Класс TCriticalSection находится в модуле SyncObjs. Модуль SyncObjs обычно не включен в Delphi 4 Standard Edition. Если Вы используете Delphi 4, то Вы можете загрущить SyncObjs.pas файл с web сайта Indy. Этот файл не срдержит всей функциональности реализованной Борланд, но имеет реализацию класса TCriticalSection.



Содержание раздела