Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 6004.doc
Скачиваний:
21
Добавлен:
30.04.2022
Размер:
1.29 Mб
Скачать

3.2.7.1Критические секции в программном интерфейсе windows

Синхронизация задач в рамках одного процесса осуществляется с помощью объекта "критическая секция". Критическая секция создается как структура типа CRITICAL_SECTION в области глобальных переменных. Перед использованием критическая секция должна быть проинициализирована:

VOID InitializeCriticalSection (LPCRITICAL_SECTION lpCritSec);

Удаление критической секции после ее использования:

VOID DeleleCriticalSection (LPCRITICAL_SECTION lpCritSec);

После удаления доступ к критической секции невозможен.

Критическая секция управляет доступом задач к фрагментам кодов с разделенными данными. Критический участок окаймляется функциями входа и выхода из критической секции

VOID EnterCriticalSection (LPCRITICAL_SECTION lpCritSec); VOID LeaveCriticalSection (LPCRITICAL_SECTION lpCritSec);

Для последовательного доступа к ресурсу из задач, созданных разными процессами, используется объект Mutex (Mutually Exclusive - взаимоисключающий). Данный объект, как и событие, может находиться в отмеченном и в неотмеченном состоянии. Когда процесс становится владельцем объекта Mutex, он переводится в неотмеченное состояние, а все остальные процессы, которые обращаются за доступом к этому объекту, должны ожидать, пока он станет отмеченным.

Создание объекта Mutex:

НANDLE CreateMutex (LPSECURITY_ATTRIBUTES lpSecurityAttr,

BOOL bInitialOwner,

LPCTSTR lpName);

Первый параметр передает указатель на структуру с атрибутами системы защиты (NULL - стандартные атрибуты). Второй параметр - флаг начального состояния: True - задача, создавшая Mutex, сразу же захватывает вход в критическую секцию, False - объект создан, но никому не принадлежит. Третий параметр содержит адрес строки с уникальным именем объекта. Другие процессы должны открыть объект Mutex с тем же самым именем:

НANDLE OpenMutex (DWORD fdwAccess,

BOOL bInНerit,

LPCTSTR lpName);

Первый параметр - флаги доступа:

EVENT_ALL_ACCESS - все флаги доступа,

SYNCНRONIZE - разрешить ожидание Mutex

Второй параметр - логический флаг, который разрешает или запрещает наследование идентификаторов.

Третий параметр - указатель на символьную строку, которая содержит имя объекта. Чтобы завладеть Mutex необходимо выполнить функцию WaitForSingleObject (НMutex).

Освобождение объекта выполняется при завершении критической секции с помощью функции:

BOOL ReleaseMutex (НANDLE НMutex);

Полное уничтожение Mutex выполняется закрытием идентификатора функцией CloseНandle.

3.2.7.2Блокирующие функции

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

long InterLockedIncrement (LPLONG lpAddend);

long InterLockedDecrement (LPLONG lpAddend);

long InterLockedExcНange (LPLONG lpTarget,

long lNewValue);

3.2.8.Семафоры

Механизм взаимоисключения может быть реализован с помощью семафоров: двоичных и считающих. С семафором связаны инициализация и две операции: P(s) - закрытие, V(s) - открытие. Двоичный семафор имеет два состояния - 0 и 1. Закрытый семафор находится в состоянии 0, открытый - в состоянии 1. При инициализации семафора определяется его начальное состояние:

ИнициализацияСемафора(s, 1);

или

ИнициализацияСемафора(s, 0);

Операция P(s):

if s = 1

then s := 0 { закрыть семафор }

else блокировать обратившийся процесс по s;

установить на выполнение готовый процесс;

Операция V(s):

if список процессов, ожидающих s, не пуст

then деблокировать процесс, ожидающий S

else s := 1; { открыть семафор }

Критический участок окаймляется операциями P(s) и V(s). Таким образом, двоичный семафор обеспечивает логику работы с событием (Event).

Считающие семафоры полезны при выделении одиночного ресурса из общего пула. При инициализации в s заносят количественный показатель объема ресурсов пула. P(s) вызывает уменьшение счетчика ресурса на единицу, а V(s)- увеличение счетчика на единицу, т.е.

операция P(s):

if s > 0

then s := s - 1

else ожидать на s;

операция V(s):

if список процессов, ожидающих s не пуст

then разрешить одному процессу работать

else s := s + 1;

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

Когда семафор становится отмеченным, возобновляется работа задачи. Семафор является объектом, с помощью которого можно синхронизировать задачи, принадлежащие разным процессам: одна задача должна создать семафор, а все остальные - открыть. Функция создания семафора:

НANDLE CreateSemapНore (LPSECURITY_ATTRIBUTES lpSecurityAttr,

long lInitialCount,

long lMaximumCount,

LPCTSTR lpName);

Первым передается указатель на структуру атрибутов системы безопасности. Второй параметр содержит начальное значение счетчика ресурсов. Третий параметр задает максимальное значение счетчика ресурсов. Последним функции передается указатель на символьную строку с уникальным именем семафора.

Открытие семафора:

НANDLE OpenSemapНore (DWORD dwAccess,

BOOL bInНerit,

LPCTSTR lpName);

С помощью первого параметра семафору назначаются флаги доступа

SEMAPНORE_ALL_ACCESS - полный доступ

SEMAPНORE_MODIFY_STATE - разрешение изменения счетчика ресурсов

SYNCНRONIZE - разрешено использование идентификатора в функциях ожидания.

Второй параметр содержит логический флаг разрешения/запрещения наследования идентификатора.

Увеличение счетчика ресурсов (освобождение) выполняется функцией:

BOOL ReleaseSemapНore(НANDLE НSemapНore,

long lReleaseCount,

LPLONG lpPreviouseCount);

Первый параметр - идентификатор семафора; второй параметр - число единиц освобождаемого ресурса; третий параметр - адрес переменной, в которую будет записано предыдущее значение счетчика. Значение lReleaseCount должно быть больше нуля. Если освобождение ресурса состоялось, функция возвращает True. Отметим, что только эта функция позволяет определить количество ресурса семафора.

Уменьшение счетчика (выделение ресурса) выполняется функциями WaitForSingleObject и WaitForMultipleObject.

Задачи, решаемые с помощью семафоров:

1. Взаимоисключение

Два процесса совместно используют общий ресурс. Программный код, на котором реализовано обращение к общему ресурсу, обозначен "критический_участок_1" для первого процесса и "критический_участок_2" для второго. Последовательность входа на критический участок несущественна.

program P1;

var Активный: семафор;

procedure Process1;

begin

while true do

begin ....

P(Активный);

критический_участок_1;

V(Активный); ....

end

end;

procedure Process2;

begin

while true do

begin ....

P(Активный);

критический_участок_2;

V(Активный); ....

end

end;

begin

ИнициализацияСемафора(Активный, 1);

parbegin

Process1;

Process2

parend

end.

2. Синхронизация процессов

Когда процесс выдает запрос на обмен данными с внешним устройством, он блокирует себя в ожидании завершения операции ввода/вывода. Заблокированный процесс должен быть активизирован каким-либо другим процессом. Например, пусть одному процессу необходимо получить уведомление о наступлении некоторого события. Другой процесс может обнаружить, что данное событие произошло.

program P2;

var Событие: семафор;

procedure Process1;

begin ....

P(Событие);

....

end;

procedure Process2; begin

....

V(Событие);

....

end;

begin

ИнициализацияСемафора(Событие, 1);

parbegin

Process1;

Process2

parend

end.

3. "Производитель-потребитель"

Производитель генерирует информацию, потребитель - использует. Пусть они взаимодействуют при помощи одной разделяемой переменной BUFFER, то есть производитель вычисляет некоторое значение и помещает его по адресу переменной, а потребитель после этого считывает значение переменной BUFFER.

program P3;

var Доступ: семафор;

ЧислоЗанесено: семафор;

BUFFER: integer;

procedure Process1; { производитель }

var Результат: integer;

begin

while true do

begin

вычисление Результата;

Р(Доступ);

BUFFER := Результат;

V(Доступ);

V(ЧислоЗанесено)

end

end;

procedure Process2; { потребитель }

var Результат: integer;

begin

while true do

begin

P(ЧислоЗанесено);

P(Доступ);

Результат := BUFFER;

V(Доступ);

вывести Результат

end

end;

begin

ИнициализацияСемафора (Доступ, 1);

ИнициализацияСемафора (ЧислоЗанесено, 0);

parbegin

Process1;

Process2

parend

end.