Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Синхронизация с помощью критических секций




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

1) InitializeCriticalSection (p);

2) EnterCriticalSection (p);

3) TryEnterCriticalSection (p);

4) LeaveCriticalSection (p);

5) DeleteCriticalSection (p).

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

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

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

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

По окончании работы с CS все системные ресурсы, использованные этим объектом, освобождаются путем вызова DeleteCriticalSection.

Объекты типа Critical_Section не являются объектами ядра ОС, поэтому работа с ними происходит быстрее, чем с объектами синхронизации, которые являются объектами ядра ОС. Это происходит потому, что обращение к объектам ядра требует дополнительных затрат процессорного времени для переключения контекстов потоков из пользовательского режима в защищенный режим работы ядра и обратно. Поэтому при разработке многопоточных приложений для решения задачи взаимного исключения между потоками одного и того же процесса, как правило, используются критические секции.

Мьютексы.

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

Мьютекс находится в сигнальном, то есть свободном состоянии, если он не принадлежит ни одному потоку. В противном случае, мьютекс находится в несигнальном, занятом, состоянии. Это состояние говорит о том, что один из потоков использует разделяемый ресурс. Одновременно, мьютекс может принадлежать только одному потоку. Остальные потоки находятся в состоянии ожидания освобождения мьютекса, то есть перехода его в сигнальное состояние. Все эти потоки обслуживаются в порядке дисциплины FIFO, то есть они становятся в очередь к мьютексу, и выбираются из очереди в соответствии с этой дисциплиной. Но в том случае, если какой-либо поток ожидает наступления некоторого асинхронного события, то есть внешнего по отношению к системе, то функции ядра системы могут исключить поток из очереди к мьютексу для обслуживания этого события. После этого, поток снова становится в очередь к мьютексу, но в её конец.

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

CreateMutex (p1, p2, p3);

· p1 – атрибуты защиты объекта;

· p2 – начальный владелец;

· p3 – имя мьютекса.

При установке p1 в NULL атрибуты защиты устанавливаются по умолчанию. В этом случае, дескриптор мьютекса не наследуется в других потоках и открыт для них.

Если p2 == true, то мьютекс сразу переходит во владение того потока, в котором был создан. Такой поток имеет все права доступа к мьютексу. Если p2 == false, то созданный мьютекс свободен.

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

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

Если мьютекс с заданным в p3 именем уже существует, то функция CreateMutex возвращает дескриптор уже созданного мьютекса, а функция GetLastError, вызванная после функции CreateMutex, возвращает значение ошибки в виде именованной константы ERROR_ALREADY_EXISTS.

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

Если поток освобождает мьютекс, которым он не владеет, то данная функция также возвращает ноль.

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

· CreateMutex (p1,p2,p3);

· OpenMutex ();

CreateMutex используется, если поток не имеет информации о том, создан, или нет, мьютекс с таким же именем в другом потоке. В этом случае, p2 нужно задавать как false, поскольку невозможно определить, какой из потоков создает мьютекс.

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

Чтобы получить доступ к уже созданному мьютексу, может использоваться OpenMutex (po1, po2, po3);

· po1 – вид доступа к мьютексу;

· po2 – определяет свойства наследования мьютекса;

· po3 – имя мьютекса.

po1 может иметь два значения:

1) полный доступ к мьютексу;

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

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

В случае успешного завершения, функция OpenMutex возвращает дескриптор открытого мьютекса, а в противном случае – ноль.

События.

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

В ОС Windows, событие описывается объектами ядра системы Events. Существует два типа событий:

1) события с ручным сбросом;

2) события с автоматическим сбросом.

Различие между ними состоит в том, что события с ручным сбросом можно перевести в несигнальное состояние только путем вызова функции ResetEvent (), а событие с автоматическим сбросом переходит в несигнальное состояние как при помощи ResetEvent, так и при помощи функции ожидания. При этом, если события с автоматическим сбросом ожидают несколько потоков, используя WaitForSingleObject, то из состояния ожидания освобождается только один из этих потоков.

События создаются путем вызова CreateEvent, которая имеет четыре параметра - pe1, pe2, pe3, pe4.

· pe1 – атрибуты защиты;

· pe2 – тип события;

· pe3 – начальное состояние;

· pe4 – имя события.

Чаще всего, первый параметр получает значение NULL. Если pe2 == true, то создается событие с ручным сбросом, в противном случае – с автоматическим. Если pe3==true, то начальное состояние события является сигнальным, а в противном случае – несигнальным. pe4 определяет имя события, которое позволяет обращаться к нему из потоков, выполняющихся в разных процессах. Событие может быть безымянным.

В случае удачного завершения, CreateEvent возвращает Handle, и NULL – в противном случае. Если событие с указанным в pe4 именем уже существует, то CreateEvent возвращает дескриптор существующего события, а GetLastError будет возвращать ERROR_ALREADY_EXISTS.

Для перевода любого события в сигнальное состояние используется функция SetEvent (Handle). При успешном завершении возвращается не ноль, и ноль – в противном случае.

Функция ResetEvent (Handle) используется для перевода любого события в несигнальное состояние. Возвращает аналогичные значения.

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

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

OpenEvent (po1, po2, po3);

· po1 – определяет флаги доступа;;

· po2 – режим наследования;

· po3 – имя события.

Смысл второго и третьего параметров аналогичен этим параметрам в мьютексе.

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

Флаг a означает полный доступ – поток может выполнять над событием любые действия. Флаг b называется модификацией состояния. Он означает, что поток может использовать функции SetEvent и ResetEvent для изменения состояния события. Флаг c называется синхронизацией. Поток может использовать события в функциях ожидания.

Семафоры.

Объект «семафор» в ОС Windows описывается объектом ядра Semaphore. Семафор находится в сигнальном состоянии, если его значение, называемое счетчиком семафора, больше нуля. В противном случае, семафор находится в несигнальном состоянии. Потоки, ожидающие сигнального состояния семафора, выстраиваются в очередь к нему, и обслуживаются в соответствии с дисциплиной FIFO. Если какой-либо из этих потоков ожидает наступления асинхронного события, то функции ядра могут исключить поток из очереди к семафору для обслуживания этого события (при его наступлении). После этого поток переводится в конец очереди к семафору.

Семафоры создаются вызовом функции CreateSemaphore (ps1, ps2, ps3, ps4).

· ps1 определяет атрибуты защиты объекта;

· ps2 определяет начальное значение счетчика семафора, которое должно быть не меньше нуля, и не больше его максимального значения, которое задается параметром ps3.

· ps4 задает имя семафора, либо может иметь значение пустой строки. В этом случае создается безымянный семафор.

В случае успешного завершения возвращается Handle созданного объекта, и ноль – в противном случае. Если семафор с заданным именем уже существует, то функция возвращает дескриптор ранее созданного семафора, а GetLastError возвращает ERROR_ALREADY_EXISTS.

Значение счетчика семафора уменьшается на единицу при его использовании в функции ожидания. Увеличить значение счетчика можно путем вызова ReleaseSemaphore(Handle, pr2, pr3).

· Handle – дескриптор семафора;

· pr2 – целое положительное число, на которое увеличивается значение семафора;

· pr3 – предыдущее значение семафора.

В случае успешного завершения, функция возвращает ненулевое значение, и нулевое – в противном случае.

Если значение счетчика семафора в сумме с pr2 оказывается больше ps3, то данная функция ReleaseSemaphore возвращает ноль, и значение счетчика не меняется.

Доступ к существующему семафору можно открыть либо путем вызова CreateSemaphore (ps1, ps2, ps3, ps4), либо вызова OpenSemaphore(po1, po2, po3).

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

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

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

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

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





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


Дата добавления: 2016-03-25; Мы поможем в написании ваших работ!; просмотров: 990 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Надо любить жизнь больше, чем смысл жизни. © Федор Достоевский
==> читать все изречения...

2300 - | 1987 -


© 2015-2024 lektsii.org - Контакты - Последнее добавление

Ген: 0.011 с.