Критические секции (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.