Категории
Самые читаемые
💎Читать книги // БЕСПЛАТНО // 📱Online » Компьютеры и Интернет » Программирование » Системное программирование в среде Windows - Джонсон Харт

Читаем без скачивания Системное программирование в среде Windows - Джонсон Харт

Читать онлайн Системное программирование в среде Windows - Джонсон Харт

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 66 67 68 69 70 71 72 73 74 ... 142
Перейти на страницу:

Один из возможных вариантов правильного решения заключается в том, чтобы защитить циклы ожидания при помощи мьютекса или объекта CRITI-CAL_SECTION, как показано в приведенном ниже фрагменте программного кода:

/* Уменьшаем значение счетчика семафора на 2. */

EnterCriticalSection(&csSem);

WaitForSingleObject(hSem, INFINITE);

WaitForSingleObject(hSem, INFINITE);

LeaveCriticalSection (&csSem);

ReleaseSemaphore(hSem, 2, &PrevCount);

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

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

Полное решение проблемы множественных циклов ожидания предлагается в упражнении 10.11. 

Проектировать семафоры Windows было бы гораздо удобнее, если бы существовала возможность выполнять множественные циклы ожидания в виде одной атомарной операции (atomic multiple-wait operation).

События

Последним из рассматриваемых нами типов объектов синхронизации ядра являются события (events). Объекты события используются для того, чтобы сигнализировать другим потокам о наступлении какого-либо события, например, о появлении нового сообщения.

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

• Сбрасываемые вручную события (manual-reset events) могут сигнализировать одновременно всем потокам, ожидающим наступления этого события, и переводятся в несигнальное состояние программно.

• Автоматически сбрасываемые события (auto-reset event) сбрасываются самостоятельно после освобождения одного из ожидающих потоков, тогда как другие ожидающие потоки продолжают ожидать перехода события в сигнальное состояние.

События используют пять новых функций: CreateEvent, OpenEvent, SetEvent, ResetEvent и CreateEvent. 

HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpsa, BOOL bManualReset, BOOL bInitialState, LPTCSTR lpEventName) 

Чтобы создать событие, сбрасываемое вручную, необходимо установить значение параметра bManualReset равным True. Точно так же, чтобы сделать начальное состояние события сигнальным, установите равным True значение параметра bInitialState. Для открытия именованного объекта события используется функция OpenEvent, причем это может сделать и другой процесс.

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

BOOL SetEvent(HANDLE hEvent)

BOOL ResetEvent(HANDLE hEvent)

BOOL PulseEvent(HANDLE hEvent) 

Поток может установить событие в сигнальное состояние, используя функцию SetEvent. Если событие является автоматически сбрасываемым, то оно автоматически возвращается в несигнальное состояние уже после освобождения только одного из ожидающих потоков. В отсутствие потоков, ожидающих наступления этого события, оно остается в сигнальном состоянии до тех пор, пока такой поток не появится, после чего этот поток сразу же освобождается. Заметьте, что таким же образом ведет себя семафор, максимальное значение счетчика которого установлено равным 1.

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

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

Примечание

Хотя в книгах многих авторов и даже в некоторых документах Microsoft (см. примечания в разделе MSDN, содержащем описание функции PulseEvent) рекомендуется избегать использования функции PulseEvent, лично я считаю эту функцию не только полезной, но и существенно важной, как это следует из обсуждения многочисленных примеров, приведенных в двух следующих главах.

Следует отметить, что функция PulseEvent становится полезной лишь после того, как сбрасываемое вручную событие установлено в сигнальное состояние с помощью функции SetEvent. Будьте внимательны, когда используете функцию WaitForMultipleObjects для ожидания перехода в сигнальное состояние всех событий. Ожидающий поток освободится только тогда, когда одновременно все события будут находиться в сигнальном состоянии, и некоторые из событий, находящихся в сигнальном состоянии, могут быть сброшены, прежде чем поток освободится.

В упражнении 8.5 вам предлагается изменить программу sortMT (программа 7.2) за счет использования в ней событий. 

Переменные условий (condition variables) Pthreads в некоторой степени сравнимы с событиями, но используются в сочетании с мьютексами. Такой способ их использования в действительности является очень плодотворным и будет описан в главе 10. Для создания и уничтожения переменной условий используются, соответственно, системные вызовы pthread_cond_init и pthread_cond_destroy. Функциями ожидания являются pthread_cond_wait и pthread_cond_timedwait. Системный вызов pthread_cond_signal осуществляет возврат после освобождения одного ожидающего потока аналогично Windows-функции PulseEvent в случае автоматически сбрасываемых событий, тогда как вызов pthread_cond_broadcast сигнализирует всем ожидающим потокам, и поэтому его можно сопоставить функции PulseEvent, применяемой к сбрасываемому вручную событию. Эквивалентов функций PulseEvent и ResetEvent, используемых в случае сбрасываемых вручную событий, не существует.

Обзор: четыре модели использования событий

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

Предостережение

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

В табл. 8.1 описаны четыре возможные ситуации.

Таблица 8.1. Сводная таблица свойств событий 

Автоматически сбрасываемые события Сбрасываемые вручную события SetEvent Освобождается строго один поток. Если в этот момент ни один из потоков не ожидает наступления события, то поток, который первым перейдет в состояние ожидания следующих событий, будет сразу же освобожден. После этого событие немедленно автоматически сбрасывается. Освобождаются все потоки, которые в настоящее время ожидают наступления события. Событие остается в сигнальном состоянии до тех пор, пока не будет сброшено каким-либо потоком. PulseEvent Освобождается строго один поток, но только в том случае, если имеется поток, ожидающий наступления события. Освобождаются все потоки, которые в этот момент ожидают наступления события, если таковые имеются, после чего событие сбрасывается и переходит в несигнальное состояние. 

Образно говоря, автоматически сбрасываемое событие — это дверь, снабженная пружиной, которая обеспечивает автоматическое закрытие двери, в то время как вручную сбрасываемое событие можно уподобить двери, в которой пружина отсутствует и которая, будучи раз открытой, продолжает оставаться в таком состоянии. Используя эту метафору, можно сказать, что функция PulseEvent открывает дверь и закрывает ее сразу же после того, как через нее проходят одна (автоматически сбрасываемые события) или все (вручную сбрасываемые события) ожидающие потоки. Функция SetEvent открывает дверь и освобождает ее.

1 ... 66 67 68 69 70 71 72 73 74 ... 142
Перейти на страницу:
На этой странице вы можете бесплатно скачать Системное программирование в среде Windows - Джонсон Харт торрент бесплатно.
Комментарии