Дополнительные механизмы синхронизации
Критические секции
Критические секции – это механизм, предназначенный для синхронизации потоков внутри одного процесса. Как и мутекс, критическая секция может в один момент времени принадлежать только одному потоку, однако, она предоставляет более быстрый и эффективный механизм, чем мутексы. Перед использованием критической секции необходимо инициализировать её функцией:
procedure InitializeCriticalSection(
var lpCriticalSection: TRTLCriticalSection
); stdcall;
После создания объекта поток, перед доступом к защищаемому ресурсу должен вызвать функцию:
procedure EnterCriticalSection(
var lpCriticalSection: TRTLCriticalSection
); stdcall;
Если в этот момент ни один из потоков в процессе не владеет объектом, то поток становится владельцем критической секции и продолжает выполнение. Если секция уже захвачена другим потоком то выполнение потока, вызвавшего функцию приостанавливается до её освобождения.
Поток, владеющий критической секцией, может повторно вызывать функцию EnterCriticalSection без блокирования своего исполнения. По завершению работы с защищаемым ресурсом поток должен вызвать функцию:
procedure LeaveCriticalSection(
var lpCriticalSection: TRTLCriticalSection
); stdcall;
Эта функция освобождает объект независимо от количества предыдущих вызовов потоком функции EnterCriticalSection. Если имеются другие потоки, ожидающие освобождения секции, один из них становится её владельцем и продолжает исполнение. Если поток завершился, не освободив критическую секцию, её состояние становится неопределенным, что может вызвать блокировку работы программы.
Имеется возможность попытаться захватить объект без замораживания потока. Для этого служит функция:
function TryEnterCriticalSection(
var lpCriticalSection: TRTLCriticalSection
): BOOL; stdcall;
Она проверяет, захвачена секция ли в момент её вызова. Если да – функция возвращает FALSE, в противном случае – захватывает секцию и возвращает TRUE.
По завершении работы с критической секцией, она должна быть уничтожена вызовом функции:
procedure DeleteCriticalSection(
var lpCriticalSection: TRTLCriticalSection
); stdcall;
Рассмотрим пример приложения, осуществляющего в нескольких потоках загрузку данных по сети. Глобальные переменные BytesSummary и TimeSummary хранят общее количество загруженных байт и время загрузки. Эти переменные каждый поток обновляет по мере считывания данных. Для предотвращения конфликтов приложение должно защитить общий ресурс при помощи критической секции.
Code: |
var // Глобальные переменные CriticalSection: TRTLCriticalSection; BytesSummary: Cardinal; TimeSummary: TDateTime; AverageSpeed: Float;
...
// При инициализации приложения InitializeCriticalSection(CriticalSection); BytesSummary := 0; TimeSummary := 0; AverageSpeed := 0;
//В методе Execute потока, загружающего данные. repeat BytesRead := ReadDataBlockFromNetwork; EnterCriticalSection(CriticalSection); try BytesSummary := BytesSummary + BytesRead; TimeSummary := TimeSummary + (Now - ThreadStartTime); if TimeSummary > 0 then AverageSpeed := BytesSummary / (TimeSummary/24/60/60); finally LeaveCriticalSection(CriticalSection) end; until LoadComplete;
// При завершении приложения DeleteCriticalSection(CriticalSection); |
Delphi предоставляет класс, инкапсулирующий функциональность критической секции. Класс объявлен в модуле SyncObjs.pas
type
TCriticalSection = class(TSynchroObject)
public
constructor Create;
destructor Destroy; override;
procedure Acquire; override;
procedure Release; override;
procedure Enter;
procedure Leave;
end;
Методы Enter и Leave являются синонимами методов Acquire и Release соответственно и добавлены для лучшей читаемости исходного кода.
procedure TCriticalSection.Enter;
begin
Acquire;
end;
procedure TCriticalSection.Leave;
begin
Release;
end;
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!