Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lection 10.doc
Скачиваний:
16
Добавлен:
13.04.2015
Размер:
207.36 Кб
Скачать

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

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

8.2.1. Синхронизация потоков объектами ядра

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

Некоторые объекты ядра, как синхронизируемые, так и синхронизирующие могут находиться в двух состояниях: свободном (signaled state) и занятом (nonsignaled state). Переход из одного состояния в другое осуществляется по правилам, определённым для каждого из объектов ядра отдельно. Внутри объекта ядра находится булева переменная, которая хранит состояние объекта ядра (FALSE – занят, TRUE – свободен). Объект ядра «процесс»/«поток» пребывает в занятом (FALSE) состоянии, пока выполняется сопоставленный с ним процесс/поток, и переходит в свободное (TRUE) состояние, когда процесс/поток завершается. Кроме того, потоки, в отличие от процессов, могут существовать в свободном (TRUE) состоянии не завершаясь, а засыпая. Для введения потока в свободное (TRUE) состояние существуют Wait-функции. Они переводят потоки из режима пользователя в режим ядра. В двух состояниях могут находиться такие объекты ядра как: процессы, потоки, задания, события, семафоры, мьютексы, ожидаемые таймеры, файлы, консольный ввод, уведомления об изменении файлов.

К объектам ядра, которые можно использовать для синхронизации относятся:

Мъютексы (mutex) — это глобальные объекты, при помощи которых возможно организовать последовательный доступ к ресурсам. Сначала устанавли­вается мьютекс, затем происходит некоторое действие и после этого освобождается мьютекс. Если мьютекс установлен, любой другой поток (или процесс), который попытается установить тот же мьютекс, будет остановлен до того момента, когда мьютекс будет освобожден предыду­щим потоком (или процессом). Мьютекс может совместно использоваться различными приложениями.

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

При работе с мьютексами необходимо следовать следующим правилам:

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

  2. Если его идентификатор не равен 0, мьютекс захвачен одним из потоков и находится в занятом состоянии.

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

  4. Освободить мьютекс можно функцией ReleaseMutex.

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

Функция ReleaseMutex - это функция освобождения мьютекса.

Семафоры (semaphor) — аналогичны мьютексам, но при их работе подсчи­тывается число обращений. Семафор – синхронизирующий объект ядра, который содержит счётчик числа пользователей и два 32-разрядных целых числа со знаком: счётчик текущего числа ресурсов от 0 и до значения максимального числа ресурсов (контролируемого семафором). Посредством семафора можно разрешить доступ к ресурсу, например, не более чем трем потокам одновременно. Мьютекс это тот же семафор с максимальным значением счетчика, равным 1. Создать семафор можно функцией CreateSemaphore, а открыть его - функцией OpenSemaphore. При работе с семафорами необходимо следовать таким правилам:

  1. Если счётчик числа ресурсов равен нулю – семафор занят.

  2. Если счётчик числа ресурсов больше, чем нуль – семафор свободен.

  3. Счётчик числа ресурсов не может быть меньше нуля или больше, чем максимальное значение счётчика ресурсов.

  4. Каждое успешное ожидание семафора Wait-функцией уменьшает значение счётчика числа ресурсов на 1, т. е. поток захватывает ресурс.

  5. Функция ReleaseSemaphore увеличивает значение счётчика числа пользователей на 1, может вызываться потоком, закончившим работать с ресурсом, или при поступлении клиентского запроса.

  6. Потоки, ожидающие доступ через семафор, получают его по принципу работы стека FIFO (First Input First Output) – первый поток, ставший на очередь ожидания получит доступ первым.

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

События (event) — могут применяться как средство синхронизации потока с системными событиями, такими как пользовательские операции с файлами. Метод WaitFor класса TThread (Delphi, C++ Builder) использует событие. События могут так­же использоваться для того, чтобы активизировать одновременно не­сколько потоков. Событие – самая простая разновидность синхронизирующего объекта ядра, используется для уведомления об окончании какой-либо операции. Событие содержит счётчик числа пользователей (как и все объекты ядра) и две булевы переменные: одна сообщает тип данного объекта ядра «событие» (с авто-сбросом или без), другая – его состояние (свободен или занят). Объект ядра «событие» бывает двух типов:

  • со сбросом вручную (manual-reset events) – вызов Wait-функции никак не влияет на состояние объект ядра «событие», что позволяет возобновить сразу несколько ждущих потоков и предоставить им доступ «только для чтения».

  • с авто-сбросом (auto-reset events) – при успешном ожидании Wait-функция автоматически переводит объект ядра «событие» в занятое состояние, что позволяет возобновить только один ждущий поток, он получит доступ, как для чтения, так и для записи.

Создать объект ядра «событие» можно при помощи функции CreateEvent. В числе параметров этой функции есть два булевых параметра. Один параметр bManualReset определяет тип объекта ядра «событие». Если он равен TRUE, то объект ядра «событие» – со сбросом вручную, если FALSE – с авто-сбросом. Параметр bInitialState определяет начальное состояние объекта ядра «событие». Если он равен TRUE, то объект ядра «событие» имеет «свободное» состояние, а если он равен FALSE – «занятое» состояние.

При помощи функции OpenEvent можно открыть объект ядра «событие». Для управления состоянием объекта ядра «событие» можно использовать такие функци: SetEvent - переводит объект ядра «событие» в свободное состояние, ResetEvent - переводит объект ядра «событие» в занятое состояние. Далее в примере рассматривается использование объекта ядра «событие» с авто-сбросом

Пример 8.2. Использование объекта ядра «событие» с авто-сбросом

HANDLE hMyEvent; // глобальный дескриптор события

int WINAPI WinMain(...)

{

hMyEvent = CreateEvent( NULL, FALSE, FALSE, NULL);

HANDLE hMyThread[2];

DWORD dwThreadId;

hMyThread[0] = _beginthreadex( NULL, ColorCorrector, NULL, 0, &dwThreadId);

hMyThread[1] = _beginthreadex( NULL, ContrastCorrector, NULL, 0, &dwThreadId);

LoadImage(...);

// теперь один из потоков получит доступ к ресурсу (изображению)

SetEvent( hEvent );

...

}

//*************************************************************************

DWORD WINAPI ColorCorrection(LPVOID lpParam)

{

WaitForSingleObject( hMyEvent, INFINITE );

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

// получен доступ к изображению и выполняем коррекцию цвета изображения

SetEvent( hEvent ); // освобождаем объект ядра «событие»

return(0);

}

//*************************************************************************

DWORD WINAPI ContrastCorrector(LPVOID lpParam)

{

WaitForSingleObject( hMyEvent, INFINITE );

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

// получен доступ к изображению и выполняем коррекцию контраста изображения

SetEvent( hEvent ); // освобождаем объект ядра «событие»

return(0);

}

Некоторые замечания по примеру. В данном примере создается два дочерних потока, которые осуществляют обработку одного изображения. Цель данного примера - показать, каким образом использовать объект ядра «событие» для синхронизации доступа к одному объекту. После вызова первичным потоком функции SetEvent система возобновит выполнение только одного потока из двух. Закончив работу с данными, поток вызывает SetEvent, которая разрешает системе возобновить выполнение следующего из ждущих потоков. Использование события с авто-сбросом снимает проблему с доступом вторичных потоков к памяти, как для чтения, так и для записи.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]