Синхронизация потоков с использованием объектов ядра: Методические рекомендации по выполнению лабораторной работы, страница 2

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

Если поток, которому принадлежит мьютекс, завершится, не успев его освободить? В таком случае система считает, что произошел отказ от мьютекса, и автоматически переводит его в свободное состояние. Тогда Wait-функция возвращает WAIT_ABANDONED  вместо WAIT_OBJECT_0, сигнализируя тем самым, что мьютекс свободен, но освобожден некорректно. Выяснить, что сделал с защищенными данными бывший владелец объекта-мьютекса, увы, невозможно.

          Обычно не проверяют возвращаемое значение на WAIT_ABANDONED, потому что такое завершение потоков происходит очень редко.

                                            События

Объект «событие» не хранит информацию о том, кто его занял.

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

   ResetEvent(hEvent);     // Перевод в занятое состояние

SetEvent(hEvent);       // Перевод в свободное состояние

События бывают двух типов: с «ручным захватом» и «автоматическим захватом».

                События с автоматическим захватом  переводятся в занятое состояние

                функцией   WaitForSingleObject(...), не требуя явного вызова ResetEvent().

               Для событий с ручным захватом   требуется явно вызывать ResetEvent().

Тип и начальное состояние объекта «событие» задаются при его создании:

HANDLE CreateEvent(
                     PSECURITY_ATTRIBUTES psa,   //
Обычно NULL

                     BOOL ManualReset,           // ручнойзахват - авто

                     BOOL InitialState,          // исходно свободен-занят

                     PCTSTR pszName              // Имясобытия

);

   Параметр

Значение

      Смысл

ManualReset

 true

 с ручным захватом

 false

 с автозахватом

InitialState

 true

 изначально свободно

 false

 изначально занято

 Если имя события  ==  NULL, его можно использовать только в рамках одного процесса.

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

вызовом функции OpenEventc передачей в параметре pszName имени объекта:

    HANDLE OpenEvent( DWORD fdwAccess, BOOL fInherit, PCTSTR pszName);

Функция возвращает дескриптор только в  том случае, когда событие уже  создано каким-то процессом, в противном случае возвращается NULL.

       Ненужный более объект «событие» следует закрыть вызовом CloseHandle(hEvent).

                                              Семафоры

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

Семафор представляет собой счетчик, который считается свободным, если значение счетчика больше нуля, и занятым при нулевом значении.

Объект ядра «семафор» создается вызовом функции:

  HANDLE CreateSemaphore(

                             PSECURITY_ATTRIBUTE psa,

                             LONG   InitialCount,

                             LONG    MaximumCount,

PCTRTR  pszName

);

Параметр  MaximumCountопределяет максимальное число потоков, которые могут одновременно пользоваться разделяемым ресурсом.

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

Любой процесс может получить свой  «процессо-зависимый» дескриптор существующего объекта «семафор», вызвав OpenSemaphore:

HANDLE OpenSemaphore(DWORD fdwAccess, BOOL bInheritHandle, PCTSTR pszName);

где  обычно fdwAccess =  SEMAPHORE_ALL_ACCESS,   bInheritHandle = true.  

Функция    возвращает дескриптор только в  том случае, когда семафор уже создан каким-то процессом, в противном случае возвращается NULL.

Работа с ресурсом, охраняемым семафором, строится так. Создаем объект «семафор»:

    long MaximumCount = .... ;

  HANDLE  hSem = CreateSemaphore(NULL, MaximumCount, MaximumCount,”Sem1”);

Это не опечатка – обычно текущее значение счетчика исходно равно максимально допустимому. При каждом успешном вызове функцииWaitForSingleObject() текущее значение уменьшается на 1.

Другой процесс может получить  свой дескриптор:

    HANDLE hSemaphor = OpenSemaphore(SEMAPHORE_ALL_ACCESS,true, “Sem1”);

Запрос доступа к охраняемому ресурсу оформляется так:

DWORDTimeOut = 2000;

DWORD Result = WaitForSingleObject(hSem, TimeOut);

if(Result == WAIT_OBJECT_0)    // Доступполучен

   {

< Операциисресурсом >  

      ReleaseSemaphore(hSem,1,NULL);  // Инкрементсчетчикаресурсов - (я

//  освободил)

   }                       

else

   {

       // Доступ к ресурсу не получен

       // Можно делать что-нибудь другое 

   }

Функция WaitForSingleObjectавтоматически уменьшает значение счетчика семафора на 1, если счетчик ненулевой, или переводит поток в режим ожидания до тех пор, пока другой поток не увеличит значение счетчика семафора вызовом функции ReleaseSemaphore().

Поток увеличивает значение счетчика текущего числа ресурсов, заканчивая работу с ресурсом и  вызывая функцию ReleaseSemaphore(...):

    BOOLReleaseSemaphore( HANDLEhSem,LONGlReleaseCount,

PLONGplPreviousCount);

Параметр  lReleaseCount  указывает, на сколько увеличить счетчик текущего числа ресурсов. Это значение должно быть  > 0   (обычно 1).

                                  3.   ЗАДАНИЕ НА РАБОТУ

В программу, созданную в л.р. № 2, добавить  объекты синхронизации, используя их поочередно:

            - мьютекс;

            - событие.

            - семафор.

       Оформить подключение синхрообъектов посредством RadioGroup.