Читайте также:
|
|
Критические секции — это механизм, предназначенный для синхронизации потоков внутри одного процесса. Как и мьютекс, критическая секция может в один момент времени принадлежать только одному потоку, однако она предоставляет более быстрый и эффективный механизм, чем мьютексы. Перед использованием критической секции необходимо инициализировать ее функцией:
procedure InitializeCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;После создания объекта поток, перед доступом к защищаемому ресурсу, должен вызвать функцию:
procedure EnterCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;Если в этот момент ни один из потоков в процессе не владеет объектом, то поток становится владельцем критической секции и продолжает выполнение. Если секция уже захвачена другим потоком, то выполнение потока, вызвавшего функцию, приостанавливается до ее освобождения.
Поток, владеющий критической секцией, может повторно вызывать функцию EnterCriticalSection без блокирования своего исполнения. По завершении работы с защищаемым ресурсом поток должен вызвать функцию LeaveCriticalSection:
procedure LeaveCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;Эта функция освобождает объект независимо от количества предыдущих вызовов потоком функции EnterCriticalSection. Если имеются другие потоки, ожидающие освобождения секции, один из них становится ее владельцем и продолжает исполнение. Если поток завершился, не освободив критическую секцию, то ее состояние становится неопределенным, что может вызвать блокировку работы программы.
Имеется возможность попытаться захватить объект без замораживания потока. Для этого служит функция TryEnterCriticalSection:
function TryEnterCriticalSection(var lpCriticalSection: TRTLCriticalSection): BOOL; stdcall;Она проверяет, захвачена ли секция в момент ее вызова. Если да — функция возвращает FALSE, в противном случае — захватывает секцию и возвращает TRUE.
По завершении работы с критической секцией она должна быть уничтожена вызовом функции DeleteCriticalSection:
procedure DeleteCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;Рассмотрим пример приложения, осуществляющего в нескольких потоках загрузку данных по сети. Глобальные переменные BytesSummary и TimeSummary хранят общее количество загруженных байтов и время загрузки. Эти переменные каждый поток обновляет по мере считывания данных; для предотвращения конфликтов приложение должно защитить общий ресурс при помощи критической секции:
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;![]() | ![]() |
Защищенный доступ к переменным (Interlocked Variable Access)
Часто возникает необходимость в совершении операций над разделяемыми между потоками 32-разрядными переменными. В целях упрощения решения этой задачи Windows API предоставляет функции для защищенного доступа к ним, не требующие использования дополнительных (и более сложных) механизмов синхронизации. Переменные, используемые в этих функциях, должны быть выровнены на границу 32-разрядного слова. Применительно к Delphi это означает, что если переменная объявлена внутри записи (record), то эта запись не должна быть упакованной (packed) и при ее объявлении должна быть активна директива компилятора {$A+}. Несоблюдение данного требования может привести к возникновению ошибок на многопроцессорных конфигурациях.
type TPackedRecord = packed record A: Byte; B: Integer; end; // TPackedRecord.B нельзя использовать в функциях InterlockedXXX TNotPackedRecord = record A: Byte; B: Integer; end; {$A-} var A1: TNotPackedRecord; // A1.B нельзя использовать в функциях InterlockedXXX I: Integer // I можно использовать в функциях InterlockedXXX, так как переменные в // Delphi всегда выравниваются на границу слова безотносительно // к состоянию директивы компилятора $A {$A+} var A2: TNotPackedRecord; // A2.B можно использовать в функциях InterlockedXXX function InterlockedIncrement(var Addend: Integer): Integer; stdcall;Функция увеличивает переменную Addend на 1. Возвращаемое значение зависит от операционной системы:
Windows 98, Windows NT 4.0 и старше — возвращается новое значение переменной Addend; |
Windows 95, Windows NT 3.51:
|
Функция уменьшает переменную Addend на 1. Возвращаемое значение аналогично функции InterlockedIncrement.
function InterlockedExchange(var Target: Integer; Value: Integer): Integer; stdcall;Функция записывает в переменную Target значение Value и возвращает предыдущее значение Target.
Следующие функции для выполнения требуют Windows 98 или Windows NT 4.0 и старше.
function InterlockedCompareExchange(var Destination: Pointer; Exchange: Pointer; Comperand: Pointer): Pointer; stdcall;Функция сравнивает значения Destination и Comperand. Если они совпадают, значение Exchange записывается в Destination. Функция возвращает начальное значение Destination.
function InterlockedExchangeAdd(Addend: PLongint; Value: Longint): Longint; stdcall;Функция добавляет к переменной, на которую указывает Addend, значение Value и возвращает начальное значение Addend.
![]() | ![]() |
Резюме
Многозадачная и многопоточная среда Win32 предоставляет широкие возможности для написания высокоэффективных приложений. Однако написание приложений, использующих многопоточность и взаимодействующих друг с другом, при неаккуратном программировании может привести к их неверной работе, неоправданной загрузке и даже к блокировке всей операционной системы. Во избежание этого следуйте нижеприведенным рекомендациям:
КомпьютерПресс 9'2001
[k1]
[NE2]
[NE3]
Дата добавления: 2014-12-19; просмотров: 111 | Поможем написать вашу работу | Нарушение авторских прав |