Студопедия
Главная страница | Контакты | Случайная страница

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Критические секции

Читайте также:
  1. В работе каждой секции (подсекции) могут принять участие не более 10 человек из одного вуза. От одного научного руководителя принимаются не более двух докладов.
  2. Заседания (под)секции « » XLI Самарской областной студенческой научной конференции
  3. История создания секции.
  4. Критические давления на грунт
  5. Критические периоды развития. Тератогенные факторы среды.
  6. Критические технологии РФ
  7. Рецензент: Гилевич Е.С. - преподаватель ЦК (секции) ЭиФ при кафедре ЭС
  8. Тема 7. Критические точки, потоки и области

Критические секции — это механизм, предназначенный для синхронизации потоков внутри одного процесса. Как и мьютекс, критическая секция может в один момент времени принадлежать только одному потоку, однако она предоставляет более быстрый и эффективный механизм, чем мьютексы. Перед использованием критической секции необходимо инициализировать ее функцией:

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 хранят общее количество загруженных байтов и время загрузки. Эти переменные каждый поток обновляет по мере считывания данных; для предотвращения конфликтов приложение должно защитить общий ресурс при помощи критической секции:

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;

Защищенный доступ к переменным (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 < 0, то возвращается отрицательное число, не обязательно равное Addend;
  • если Addend = 0, то возвращается 0;
  • если после изменения Addend > 0, то возвращается положительное число, не обязательно равное Addend.
function InterlockedDecrement(var Addend: Integer): Integer; stdcall;

Функция уменьшает переменную 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 предоставляет широкие возможности для написания высокоэффективных приложений. Однако написание приложений, использующих многопоточность и взаимодействующих друг с другом, при неаккуратном программировании может привести к их неверной работе, неоправданной загрузке и даже к блокировке всей операционной системы. Во избежание этого следуйте нижеприведенным рекомендациям:

  1. Если приложения или потоки одного процесса изменяют общий ресурс — защищайте доступ к нему при помощи критических секций или мьютексов.
  2. Если доступ осуществляется только на чтение — защищать ресурс не обязательно
  3. Критические секции более эффективны, но применимы только внутри одного процесса; мьютексы могут использоваться для синхронизации между процессами.
  4. Используйте семафоры для ограничения количества обращений к одному ресурсу.
  5. Используйте события (event) для информирования потока о наступлении какого-либо события.
  6. Если разделяемый ресурс — 32-битная переменная, то для синхронизации доступа к нему можно использовать функции, обеспечивающие разделяемый доступ к переменным.
  7. Многие объекты Win32 позволяют организовать эффективное слежение за своим состоянием при помощи функций ожидания. Это наиболее эффективный с точки зрения расхода системных ресурсов метод.
  8. Если ваш поток создает (даже неявно, при помощи CoInitialize или функций DDE) окна, то он должен обрабатывать сообщения. Не используйте в таком потоке функции, не позволяющие прервать ожидание по приходу сообщения с большим или неограниченным периодом ожидания. Используйте функции MsgWaitForXXX.

КомпьютерПресс 9'2001

 

[k1]

[NE2]

[NE3]




Дата добавления: 2014-12-19; просмотров: 111 | Поможем написать вашу работу | Нарушение авторских прав




lektsii.net - Лекции.Нет - 2014-2025 год. (0.007 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав