Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

счетчик гекущего числа ресурсов если его значение больше 0 (семафор свобо ден), уменьшает значение этого счетчика на 1, и вызывающий поток остается плани руемым Очень важно, что семафоры выполняют эту операцию проверки и присвое ния на уровне атомдрного доступа; иначе говоря, когда Вы запрашиваете у семафора какой-либо ресурс, операционная система проверяет, доступен ли этот ресурс, и, если да, уменьшает счетчик текущего числа ресурсов, не позволяя вмешиваться в эту опе рацию другому потоку Только после того как счетчик ресурсов будет уменьшен на 1, доступ к ресурсу сможет запросить другой поток

Если Wait-функция определяет, что счетчик текущего числа ресурсов равен 0 (се мафор занят), система переводит вызывающий поток в состояние ожидания Когда другой поток увеличит значение этого счетчика, система вспомнит о ждущем потоке и снова начнет выделять ему процессорное время (а он, захватив ресурс, уменьшит значение счетчика на

1).

Поток увеличивает значение счетчика текущего числа ресурсов, вызывая функцию

ReleaseSemaphore

BOOL ReleaseSemaphore( HANDLE hSem,

LONG lReleaseCount, PLONG p]PreviousCount);

Она просто складывает величину lReleaseCount со значением счетчика текущего числа ресурсов. Обычно в параметре lReleaseCount передают 1, но это вовсе не обяза тельно: я часто передаю в нем значения, равные или большие 2. Функция возвращает исходное значение счетчика ресурсов в *plPreviousCount Если Вас не интересует это значение (а в большинстве программ так оно и есть), передайте в параметре plPre viousCount значение

NULL.

Было бы удобнее определять состояние счетчика текущего числа ресурсов, не меняя его значение, но такой функции в Windows нет. Поначалу я думал, что вызовом ReleaseSemapbore с передачей ей во втором параметре нуля можно узнать истинное значение счетчика в переменной типа LONG, на которую указывает параметр plPre viousCount. Но не вышло: функция занесла туда пуль. Я передал во втором параметре заведомо большее число, и — тот же результат. Тогда мне стало ясно: получить значе ние этого счетчика, не изменив его, невозможно.

Мьютексы

Объекты ядра «мьютексы» гарантируют потокам взаимоисключающий доступ к един ственному ресурсу. Отсюда и произошло название этих объектов (mutual exclusion, mutex). Они содержат счетчик числа пользователей, счетчик рекурсии и переменную, в которой запоминается идентификатор потока. Мьютексы ведут себя точно так же, как и критические секции. Однако, если последние являются объектами пользователь ского режима, то мьютексы — объектами ядра. Кроме того, единственный объект-мью текс позволяет синхронизировать доступ к ресурсу нескольких потоков из разных процессов; при этом можно задать максимальное время ожидания доступа к ресурсу.

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

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

Для мьютексов определены следующие правила:

если его идентификатор потока равен 0 (у самого потока не может быть та кой идентификатор), мьютекс не захвачен ни одним из потоков и находится в свободном состоянии; если его идентификатор потока не равен 0, мьютекс захвачен одним из пото ков и

находится в занятом состоянии; в отличис от других объектов ядра мьютексы могут нарушать обычные прави ла,

действующие в операционной системе (об этом — чуть позже)

Для использования объекта-мьютекса один из процессов должен сначала создать его вызовом CreateMutex:

HANDLE CreateMutex( PSECURITY_ATTRIBUTES psa, BOOL fIniLialOwner, PCTSTR pszName);

O параметрах psa и pszName я рассказывал в главе 3. Разумеется, любой процесс может получить свой («процессо-зависимый») описатель существующего объекта «мьютекс»,

вызвав OpenMutex:

HANDLE OpenMutex( DWORD fdwAccess, 800L bInheritHandle, PCTSTR pszName);

Параметр fInitialOwner опрсдсляст начальное состояние мъютекса. Если в нем пе редается FALSE (что обычно и бывает), объект-мьютекс не принадлежит ни одному из потоков и поэтому находится в свободном состоянии. При этом его идентифика тор потока и счетчик рекурсии равны 0 Если же в нем передается TRUE, идентифи катор потока, принадлежащий мьютексу, приравнивается идентификатору вызываю щего потока, а счетчик рекурсии получает значение 1. Поскольку теперь идентифи катор потока отличен от 0, мьютекс изначально находится в занятом состоянии.

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

Если Wait-функция определяет, что у мьютекса идентификатор потока не равен 0 (мьютекс занят), вызывающий поток переходит в состояние ожидания. Система за поминает это и, когда идентификатор обнуляется, записывает в него идентификатор ждущего потока, а счетчику рекурсии присваивает значение 1, после чего ждущий поток вновь становится планируемым. Все проверки и изменения состояния объек та-мьютекса выполняются на уровне атомарного доступа.

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