Синхронизация потоков с использованием объектов ядра



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

Синхронизирующие объекты ядра принципиально доступны потокам любых процессов. Единственный их недостаток — меньшее быстродействие, поскольку для работы с ними поток должен перейти из пользовательского режима в режим ядра, на что требуется около 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; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!