Синхронизация потоков с использованием объектов ядра
Критические секции не являются объектами ядра, поэтому их можно использовать для синхронизации доступа к разделяемым ресурсам лишь в рамках одного процесса.
Синхронизирующие объекты ядра принципиально доступны потокам любых процессов. Единственный их недостаток — меньшее быстродействие, поскольку для работы с ними поток должен перейти из пользовательского режима в режим ядра, на что требуется около 1000 процессорных тактов.
Следующие объекты ядра используются для синхронизации доступа любых потоков к разделяемым ресурсам:
· мьютексы
· события
· семафоры
- таймеры ожидания
Эти объекты, подобно критической секции, «охраняют» вход в некоторый участок кода и бывают в свободном или занятом состоянии. Последнее означает, что их «захватил» некоторый поток, и другие потоки, желающие войти в свой кодовый участок, «охраняемый» данным объектом, должны ждать его освобождения.
Потоки не получают процессорного времени, пока ожидаемые ими объекты заняты.
Как только объект освободится, операционная система переводит ждущие потоки в разряд планируемых.
Потоки обычно «усыпляют себя» до освобождения синхронизирующего объекта с помощью функции:
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwTimeOut);
где hObject –дескриптор объекта, dwMilliseconds указывает, сколько времени (в миллисекундах) поток готов ждать освобождения объекта.
В качестве значения параметра dwTimeOut используют три варианта:
|
|
dwTimeOut | Действия |
0 | Функция только проверяет состояние объекта (занят или свободен) и сразу же возвращается |
Число МСек | Функция ожидает освобождения объекта не более указанного времени |
INFINITE | Время ожидания бесконечно. Если объект так и не освободится, поток никогда не получит процессорного времени |
Функция WaitForSingleObject может возвращать одно из следующих значений:
Возвращаемое значение | Описание |
WAIT _TI MEOUT | Объект не перешел в свободное состояние, но интервал времени истек |
WAIT_ABANDONED | Ожидаемый объект является мьютексом, который не был освобожден владеющим им потоком перед окончанием этого потока. Объект мьютекс автоматически переведен системой в свободное состояние. |
WAIT_OBJECT_0 | Объект перешел в свободное состояние |
WAIT_FAILED | Произошла ошибка, причину которой можно узнать, вызвав GetLastError |
Вот пример ожидания освобождения некоторого обобщенного объекта с дескриптором hObject:
DWORD dw = WaitForSlngleObject(hObject, 5000);
switch (dw)
{
case WAIT_OBJECT_0: // объект свободен
break;
case WAIT_TIMEOUT: // объект не освободился в течение 5000 мс
break;
|
|
case WAIT_FAILED: // некорректный вызов функции
break;
}
Мьютексы
Объекты ядра «мьютексы» гарантируют любым потокам монопольный доступ к разделяемому ресурсу и ведут себя точно так же, как и критические секции. Мьютекс позволяет синхронизировать доступ к ресурсу для потоков из разных процессов; при этом можно задать максимальное время ожидания доступа к ресурсу.
Для использования объекта-мьютекса один из процессов должен сначала создать его вызовом функции:
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName
);
Параметр psaобычно равен NULL.
Параметр fInitialOwner определяет начальное состояние мъютекса. Если в нем передается false (что обычно и бывает), мьютекс изначально не захвачен никаким потоком и находится в свободном состоянии. Если же в нем передается true, мьютекс считается захваченным потоком, создавшим его.
Параметр pszName – имя мьютекса (char-строка), возможно пустое.
Функция возвращает дескриптор мьютекса, который имеет смысл только в данном процессе.
Любой другой процесс может получить свой «процессо-зависимый» дескриптор существующего объекта «мьютекс», вызвав OpenMutex :
|
|
HANDLE OpenMutex( DWORD fdwAccess, BOOL bInheritHandle, PCTSTR pszName);
Чтобы это выполнить, нужно знать имя мьютекса, которое просвоено ему при создании.
Поток получает доступ к разделяемому ресурсу, вызывая функцию WaitForSingleObject и передавая ей дескриптор мьютекса, который охраняет этот ресурс. Если функция определяет, что мьютекс занят, вызывающий поток переводится в состояние ожидания.
Когда ожидание мьютекса потоком успешно завершается, последний получает монопольный доступ к защищенному ресурсу. Все остальные потоки, пытающиеся обратиться к этому ресурсу, переходят в состояние ожидания.
Когда поток, занимающий ресурс, заканчивает с ним работать, он должен освободить мьютекс вызовом функции
BOO L ReleaseMutex(HANDLE hMutex);
Объект-мьютекс отличается от остальных объектов ядра тем, что занявшему его потоку передаются права на владение им. Прочие объекты могут быть либо свободны, либо заняты — вот, собственно, и все. А объекты-мьютексы способны еще и запоминать, какому потоку они принадлежат. Если какой-то посторонний поток попытается освободить мьютекс вызовом функции ReleaseMutex , то она, проверив идентификаторы потоков и обнаружив их несовпадение, ничего делать не станет, а просто вернет FALSE.
|
|
Если поток, которому принадлежит мьютекс, завершится, не успев его освободить? В таком случае система считает, что произошел отказ от мьютекса, и автоматически переводит его в свободное состояние. Тогда Wait-функция возвращает WAIT_ABANDONED вместо WAIT_OBJECT_0, сигнализируя тем самым, что мьютекс свободен, но освобожден некорректно. Выяснить, что сделал с защищенными данными бывший владелец объекта-мьютекса, увы, невозможно.
Обычно не проверяют возвращаемое значение на WAIT_ABANDONED, потому что такое завершение потоков происходит очень редко.
События
Объект «событие» не хранит информацию о том, кто его занял.
Переключение состояния объекта осуществляется вызовом функций:
ResetEvent ( hEvent ); // Перевод в занятое состояние
Дата добавления: 2019-07-15; просмотров: 235; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!