ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 02.04.2021
Просмотров: 272
Скачиваний: 1
5.3. Что такое семафоры
193
5.3. Что такое семафоры
Семафор
— это механизм синхронизации, который можно использовать для управ
ления отношениями между параллельно выполняющимися программными компо
нентами и реализации стратегий доступа к данным. Семафор — это переменная спе
циального вида, которая может быть доступна только для выполнения узкого диапа
зона операций. Семафор используется для синхронизации доступа процессов
и потоков к разделяемой модифицируемой памяти или для управления доступом
к устройствам или другим ресурсам. Семафор можно рассматривать как ключ к ресур
сам. Этим ключом может владеть в любой момент времени только один процесс или
поток. Какая бы задача ни владела этим ключом, он надежно запирает (блокирует)
нужные ей ресурсы для ее монопольного использования. Блокирование ресурсов за
ставляет другие задачи, которые желают воспользоваться этими ресурсами, ожидать
до тех пор, пока они не будут разблокированы и снова станут доступными. После раз
блокирования ресурсов следующая задача, ожидающая семафор, получает его и дос
туп к ресурсам. Какая задача будет следующей, определяется стратегией планирова
ния, действующей для данного потока или процесса.
5.3.1. Операции по управлению семафором
Как упоминалось выше, к семафору можно получить доступ только с помощью спе
циальных операций, подобных тем, которые выполняются с объектами. Это операции
декремента,
P()
, и инкремента,
V()
. Если объект
Mutex
представляет собой семафор,
то логика реализации операций
P(Mutex)
и
V(Mutex)
может выглядеть таким образом:
P(Mutex)
if(Mutex > 0){
Mutex--;
}
else {
Блокирование
по
объекту
Mutex;
}
V(Mutex)
if(
Очередь
доступа
к
объекту
Mutex
не
пуста
){
Передача
объекта
Мьютекс
следующей
задаче
;
}
else{
Mutex++;
}
Реализация зависит от конкретной системы. Эти операции неделимы, т.е. их не
возможно прервать. Если операцию
P()
попытаются выполнить сразу несколько за
дач, то лишь одна из них получит разрешение продолжить работу. Если объект
Mutex
был уже декрементирован, то задача будет заблокирована и займет место в очереди.
Операция
V()
вызывается задачей, которая имеет доступ к объекту
Mutex
. Если полу
чения доступа к объекту
Мьютекс
ожидают другие задачи, он “передается” следующей
задаче из очереди. Если очередь задач пуста, объект
Mutex
инкрементируется.
Операции с семафором могут иметь другие имена:
Операция
P()
:
Операция
V()
:
lock()
unlock()
194
Глава 5. Синхронизация параллельно выполняемых задач
Значение семафора зависит от его типа.
Двоичный семафор
будет иметь значение
0
или
1
.
Вычислительный семафор
(определяющий лимиты ресурсов для процессов, получающих
доступ к ним) может иметь некоторое неотрицательное целочисленное значение.
Стандарт POSIX определяет несколько типов семафоров. Эти семафоры исполь
зуются процессами или потоками. Типы семафоров (а также их некоторые основные
операции) перечислены в табл. 5.1.
Таблица 5.1. Типы семафоров, определенные стандартом POSIX
Тип семафора
Пользователь
Описание
Мьютексный семафор
Процессы или
потоки
Механизм, используемый для реализации вза
имного исключения в критическом разделе кода
Блокировка для обеспе
чения чтения и записи
Процессы или
потоки
Механизм, используемый для реализации страте
гии доступа для чтения и записи среди потоков
Условная переменная
Процессы или
потоки
Механизм, используемый для уведомления по
токов о том, что произошло событие.
Событийный мьютекс остается заблокирован
ным потоком до тех пор, пока не будет получен
соответствующий сигнал
Несколько условных
переменных
Процессы или
потоки
Аналогичен событийному мьютексу, но включа
ет несколько событий или условий
Операционные системы, которые не противоречат спецификации Single UNIX
Specification или стандарту POSIX Standard, поддерживают реализацию семафоров,
которые являются частью библиотеки
libpthread
(соответствующие функции объ
явлены в заголовке
pthread.h
).
5.3.2. Мьютексные семафоры
Стандарт POSIX определяет мьютексный семафор, используемый потоками и про
цессами, как объект типа
pthread_mutex_t
. Этот мьютекс обеспечивает базовые опе
рации, необходимые для функционирования практического механизма синхронизации:
•
инициализация;
•
запрос на монопольное использование;
•
отказ от монопольного использования;
•
тестирование монопольного использования;
•
разрушение.
Функции класса
pthread_mutex_t
, которые используются для выполнения этих
базовых операций, перечислены в табл. 5.2. Во время
инициализации
выделяется па
мять, необходимая для функционирования мьютексного семафора, и объекту сема
фора присваивается некоторое начальное значение. Для двоичного семафора на
чальным может быть значение
0
или
1
. Начальным значением вычислительного се
мафора может быть неотрицательное число, которое представляет количество
5.3. Что такое семафоры
195
доступных ресурсных единиц. Семафорное значение можно использовать для представ
ления предельного количества запросов, которое способна обработать программа в одном
сеансе. В отличие от обычных переменных, в инициализации которых сомневаться не
приходится, факт инициализации мьютекса с помощью вызова соответствующей функции
гарантировать невозможно. Чтобы убедиться в том, что мьютекс проинициализирован,
необходимо после вызова операции инициализации принять некоторые меры предос
торожности (например, проверить значение, возвращаемое соответствующей мьютекс
ной функцией, или значение переменной errno). Системе не удастся создать мьютекс, если
окажется, что занята память, предусмотренная ранее для мьютексов, или превышено
допустимое количество семафоров, или семафор с данным именем уже существует,
или же имеет место какаято другая проблема, связанная с выделением памяти.
Таблица 5.2. Функции класса
pthread_mutex_t
Мьютексные операции
Прототипы функций (макросы)
#include <pthread.h>
Инициализация
int pthread_mutex_init(
pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex =
PTHREAD_MUTEX_INITIALIZER;
Запрос на монопольное
использование
<time.h>
int pthread_mutex_lock(
pthread_mutex_t *mutex);
int pthread_mutex_timedlock(
pthread_mutex_t *restrict mutex,
const struct tiemspec *restrict abs_timeout);
Отказ от монопольного
использования
int pthread_mutex_unlock(
pthread_mutex_t *mutex);
Тестирование монопольного
использования
int pthread_mutex_trylock(
pthread_mutex_t *mutex);
Разрушение
int pthread_mutex_destroy(
pthread_mutex_t *mutex);
Подобно потокам, мьютекс библиотеки Pthread имеет атрибутный объект
(он рассматривается ниже), который инкапсулирует все атрибуты мьютекса. Этот
атрибутный объект можно передать функции инициализации, в результате чего бу
дет создан мьютекс с атрибутами, заданными с помощью этого объекта. Если при
инициализации атрибутный объект не используется, мьютекс будет инициализирован
значениями, действующими по умолчанию. Объект типа
pthread_mutex_t
инициа
лизируется как деблокированный и закрытый.
Закрытый
мьютекс разделяется между
потоками одного процесса.
Разделяемый
мьютекс совместно используется потоками
нескольких процессов. При использовании атрибутов, действующих по умолчанию,
мьютекс может быть инициализирован статически для статических мьютексных
объектов с помощью следующего макроса:
pthread_mutext Mutex = PTHREAD_MUTEX_INITIALIZER;
196
Глава 5. Синхронизация параллельно выполняемых задач
Этот метод менее затратный, но в нем не предусмотрено проверки ошибок.
Мьютекс может иметь или не иметь владельца. Операция
запроса на монопольное ис
пользование
предоставляет право владения мьютексом вызывающему потоку или про
цессу. После того как мьютекс обрел владельца, поток (или процесс) получает моно
польный доступ к запрашиваемому ресурсу. При попытке завладеть “уже занятым”
мьютексом (путем вызова этой операции), совершенной любыми другими потоками
или процессами, они будут заблокированы до тех пор, пока мьютекс не станет доступ
ным. При освобождении мьютекса следующий (по очереди) поток или процесс
(который был заблокирован) деблокируется и получает право собственности на этот
мьютекс. И освободить его может только поток, получивший данный мьютекс во вла
дение с помощью функции
pthread_mutex_lock()
. Можно также использовать
синхронизированную версию этой функции. В этом случае, если мьютекс несвободен,
то процесс или поток будет ожидать в течение заданного промежутка времени. Если
мьютекс за это время не освободится, то процесс или поток продолжит выполнение.
Операция тестирования монопольного использования
предназначена для проверки дос
тупности мьютекса. Если он занят, функция возвращает соответствующее значение.
Достоинство этой операции состоит в том, что поток или процесс (в случае недоступ
ности мьютекса) не блокируется и может продолжать выполнение. Если же мьютекс
свободен, вызывающему потоку или процессу предоставляется право владения за
прашиваемым мьютексом.
При выполнении
операции разрушения
освобождается память, связанная с мьютек
сом. Память не освобождается, если у мьютекса есть владелец или если поток (или
процесс) ожидают права на владение им.
5.3.2.1. Использование мьютексного атрибутного объекта
Мьютексный объект типа
pthread_mutex_t
можно использовать вместе с атрибут
ным объектом подобно атрибутному объекту потока. Мьютексный атрибутный объект
инкапсулирует все атрибуты объекта мьютекса. После инициализации его могут исполь
зовать несколько мьютексных объектов, передавая в качестве параметра функции
pthread_mutex_init()
. Мьютексный атрибутный объект определяет ряд функций,
используемых для установки таких атрибутов, как предельный приоритет, протокол
и тип. Эти и другие функции мьютексного атрибутного объекта перечислены в табл. 5.3.
Таблица 5.3. Функции доступа к мьютексному атрибутному объекту
Прототипы функций
Описание
#include <pthread.h>
int pthread_mutexattr_init
(pthread_mutexattr_t * attr);
Инициализирует мьютексный атрибутный объ
ект, заданный параметром
attr
, значениями,
действующими по умолчанию для всех атрибу
тов, определяемых реализацией
int pthread_mutexattr_destroy
(pthread_mutexattr_t * attr);
Разрушает мьютексный атрибутный объект, за
данный параметром
attr
, в результате чего он
становится неинициализированным. Его мож
но инициализировать повторно с помощью
функции
pthread_mutexattr_init()
5.3. Что такое семафоры
197
Продолжение табл. 5.3
Прототипы функций
Описание
int
pthread_mutexattr_setprioceiling
(pthread_mutexattr_t * attr,
int prioceiling);
int
pthread_mutexattr_getprioceiling
(const pthread_mutexattr_t *
restrict attr,
int *restrict prioceiling);
Устанавливает и возвращает атрибут предель
ного приоритета мьютекса, заданного парамет
ром
attr
. Параметр
prioceiling
содержит
значение предельного приоритета мьютекса.
Атрибут
prioceiling
определяет минималь
ный уровень приоритета, при котором еще вы
полняется критический раздел, защищаемый
мьютексом. Значения, которые попадают в этот
диапазон приоритетов, определяются страте
гией планирования
SCHED_FIFO
int
pthread_mutexattr_setprotocol
(pthread_mutexattr_t * attr,
protocol int protocol);
int
pthread_mutexattr_getprotocol
(const pthread_mutexattr_t *
restrict attr,
int *restrict protocol);
Устанавливает и возвращает атрибут протокола
мьютекса, заданного параметром
attr
. Параметр
protocol
может содержать следующие значения:
PTHREAD_PRIO_NONE
(на приоритет и стратегию планирования пото
ка владение мьютексом
не оказывает влияния);
PTHREAD_PRIO_INHERIT
(при таком протоколе поток, блокирующий
другие потоки с более высокими приоритета
ми, благодаря владению таким мьютексом бу
дет выполняться с самым высоким приорите
том из приоритетов потоков, ожидающих ос
вобождения любого из мьютексов, которыми
владеет данный поток);
PTHREAD_PRIO_PROTECT
(при таком протоколе потоки, владеющие та
ким мьютексом, будут выполняться при наи
высших предельных значениях приоритетов
всех мьютексов, которыми владеют эти пото
ки, независимо от того, заблокированы ли дру
гие потоки по какимто из этих мьютексов)
int
pthread_mutexattr_setpshared
(pthread_mutexattr_t * attr,
int pshared);
int
pthread_mutexattr_getpshared
(const pthread_mutexattr_t *
restrict attr,
int *restrict pshared);
Устанавливает и возвращает атрибут
process-
shared
мьютексного атрибутного объекта, за
данного параметром
attr
. Параметр
pshared
может содержать следующие значения:
PTHREAD_PROCESS_SHARED
(разрешает разделять мьютекс с любыми по
токами, которые имеют доступ к выделенной
для этого мьютекса памяти, даже если эти по
токи принадлежат различным процессам
)
;
PTHREAD_PROCESS_PRIVATE
(мьютекс разделяется между потоками одного
и того же процесса)