Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
3 семестр, WinAPI, MFC.pdf
Скачиваний:
388
Добавлен:
15.06.2014
Размер:
6.17 Mб
Скачать

HANDLE hFileMapRO;

DuplicateHandle(GetCurrentProcess(),

hFileMapRW, GetCurrentProcess(), &hFileMdpRO, FILE_MAP_READ, FALSE, 0);

// вызываем функцию, которая не должна ничего записывать в проекцию файла

ReadFromTheFileMapping(hFileMapRO);

// закрываем объект "проекция файла" , доступный только для чтения

CloseHandle(hFileMapRO);

// проекция файла нам по-прежнему полностью доступна через hFileMapRW

.

.

.

// если проекция файла больше не нужна основному коду, закрываем ее

CloseHandle(hFileMapRW);

}

Изменение флагов описателя

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

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

описателем, вызовом SetHandleInformation:

BOOL

SetHandleInformation(

HANDLE

hObject,

DWORD

dwMask,

DWORD dwFlags);

 

Как видите, эта функция принимает три параметра. Первый (bObject) идентифицирует допустимый описатель. Второй (dwMask) сообщает функции, какой флаг (или флаги)

Вы хотите изменить. На сегодняшний день с каждым описателем связано два флага:

#define

HANDLE_FLAG_INHERIT

0x00000001

#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002

 

И, наконец, третий параметр функции SetHandleInformation — dwFlags — указывает, в

какое именно состояние следует перевести флаги. Например, чтобы установить флаг наследования для описателя объекта ядра:

SetHandleInformation(hobj, HANDLE_FLAG_INHERIT,

HANDLE_FLAG_INHERIT);

а чтобы сбросить этот флаг:

SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, 0);

Флаг HANDLE_FLAG_PROTECT_FROM_CLOSE сообщает системе, что

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

SetHandleInformation(hobj, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);

CloseHandle(hobj); // генерируется исключение

Если какой-нибудь поток попытается закрыть защищенный описатель,

CloseHandle приведет к исключению. Необходимость в такой защите возникает очень редко. Однако этот флаг весьма полезен, когда процесс порождает дочерний, а тот в свою очередь — еще один процесс. При этом родительский процесс может ожидать, что его "внук" унаследует определенный описатель объекта, переданный дочернему. Но тут вполне возможно, что дочерний процесс, прежде чем породить новый процесс, закрывает нужный описатель. Тогда родительский процесс теряет связь с "внуком", поскольку тот не унаследовал требуемый объект ядра. Защитив описатель от закрытия, Вы исправите ситуацию, и "внук" унаследует предназначенный ему объект.

У этого подхода, впрочем, есть один недостаток. Дочерний процесс, вызвав:

SetHandleInformation(hobj, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0);

CloseHandle(hobj);

может сбросить флаг HANDLE_FLAG_PROTECT_FROM_CLOSE и закрыть затем соответствующий описатель. Родительский процесс ставит на то, что дочерний не исполнит этот код. Но одновременно он ставит и на то, что дочерний процесс породит ему "внука", поэтому в целом ставки не слишком рискованны.

Для полноты картины стоит, пожалуй, упомянуть и функцию

GetHandleInformation:

 

BOOL

GetHandleInformation(

HANDLE

hObj,

PDWORD pdwFlags);

 

Эта функция возвращает текущие флаги для заданного описателя в переменной типа

DWORD, на которую укапывает pdwFlags. Чтобы проверить, является ли описатель наследуемым, сделайте так:

DWORD

dwFlags;

GetHandleInformation(hObj,

&dwFlags);

BOOL fHandleIsInheritable = (0 != (dwFlags & HANDLE_FLAG_INHERIT));

 

Синхронизация потоков

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

события и ожидаемые таймеры.

В общем случае поток синхронизирует себя с другим так: он засыпает, и

операционная система, не выделяя ему процессорного времени, приостанавливает его выполнение. Но прежде чем заснуть, поток сообщает системе, какое особое событие должно произойти, чтобы его исполнение возобновилось. Как только указанное событие произойдет, поток вновь получит право на выделение ему процессорного времени, и все пойдет своим чередом. Таким образом, отныне

выполнение потока синхронизировано с определенным событием.

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

Критическая секция (critical section) — это небольшой участок кода, требующий монопольного доступа к каким-то общим данным. Она представляет собой структуру, содержащую несколько флагов и какой-то объект ядра. Среди синхронизирующих объектов критические секции наиболее просты, но применимы для синхронизации потоков лишь в пределах одного процесса. Они позволяют сделать так, чтобы единовременно только один поток получал доступ к определенному региону. При входе в критическую секцию сначала проверяются флаги, и если выясняется, что она занята уже другим потоком, то выполняется обычная wait-

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

Создание. Для этого сначала сформируем в своем процессе структуру данных CRITICAL_SECTION. Она должна быть глобальной, чтобы к ней могли обращаться разные потоки. Обычно критические секции представляют собой набор глобальных переменных. Хотя структура CRITICAL_SECTION и ее элементы определены в файле WINNT.H, считайте ее содержимое «черным ящиком». Win32-функции, управляющие критическими секциями, сами инициализируют и модифицируют элементы данной структуры.

Применение. Прежде чем синхронизировать потоки с помощью критической секции, нужно инициализировать ее вызовом InitializeCriticalSection, передав адрес

структуры CRITICAL_SECTION в параметре IpCriticalSection:

VOID InitializeCnticalSection(LPCRITICAL_SECTION IpCnticalSection)

Эта функция обязательно вызывается перед обращением к EnterCriticalSection..

Предположим, SecondThread исполняется первой. Она вызывает

EnterCriticalSection, передавая ей адрес переменной-структуры g_CriticalSection:

VOID EnterCriticalSection(LPCRITICAL_SECTION IpCriticalSection)

Обнаружив, что для переменной g_CriticalSection она вызвана впервые, эта функция изменяет некоторые элементы в структуре данных .Допустим, после этого второй поток вытесняется первым, и начинается исполнение функции FirstThread. Она вызывает EnterCriticalSection, передавая ей адрес того же объекта, что и SecondThread. На этот раз

EnterCriticalSection видит, что переменная-структура g_CriticalSection уже используется, и приостанавливает исполнение первого потока. Остаток процессорного времени передается другому потоку, и операционная система больше не выделяет квантов времени первому потоку, пока он спит.

Когда второй поток получит следующий квант времени, функция SecondThread исполнит оператор: