Функция ожидания с тайм-аутом,
sem_timedwait()
, ожидает возможности уменьшить на 1 счетчик семафора (блокируется на ожидании) до момента времени
abs_timeout
. Это ожидание реализовано с помощью функции
TimerTimeout()
(native QNX API) с параметром
SIGEV_UNBLOCK
. Благодаря этой функции может быть прерван ряд состояний блокировки потоков (в том числе блокировки на семафоре, мьютексе и условной переменной).
Операции освобождения
int sem_post(sem_t* sem);
Эта операция увеличивает на единицу (инкремент) внутренний счетчик семафора, и в этом она диаметрально противоположна операции блокировки. Если перед этим значение счетчика семафора было равно 0, то один из потоков, ожидающих разблокирования семафора, переходит в состояние готовности. Из списка ожидающих потоков система выбирает самый приоритетный, а если таких несколько, то поток, дольше всех ждавший из очереди наиболее приоритетных потоков.
Эта функция имеет свой оригинал в native API QNX:
int SyncSemPost(sync_t* sync);
Фактически разница между POSIX и QNX API в вариантах этой функции состоит в регистрируемых ею ошибках.
Функция
sem_post()
сообщает о следующих ошибках:
EINVAL
— неверный дескриптор семафора
sem;
ENOSYS
— функция
sem_post()
не поддерживается системой.
В отличие от
sem_post()
, функция
SyncSemPost()
может указывать на ошибки:
EAGAIN
— недостаточно памяти для создания внутреннего объекта синхронизации;
EFAULT
— неверный указатель на семафор
sync
;
EINTR
— выполнение функции прервано сигналом;
EINVAL
— аргумент
sync
не указывает на инициированный семафор.
Как видим, функция QNX API несколько разнообразнее в плане контроля передаваемых аргументов и результата выполнения функции.
Получение статуса семафора
int sem_getvalue(sem_t* sem, int* value);
Эта функция используется преимущественно для отладки операций над семафорами. По адресу, указанному в
value
, устанавливается текущее значение счетчика семафора. Поскольку значение счетчика семафора может измениться в любой момент, то значение, которое возвращает эта функция, имеет смысл только непосредственно в точке ее вызова.
Возможные ошибки:
EINVAL
— неправильный объект семафор
sem.
Использование семафора
Как уже говорилось выше, семафор является крайне гибким и эффективным средством синхронизации, особенно удобным для построения собственных средств планирования выполнения потоков. В этом смысле семафор представляет ценность не только как самостоятельное средство синхронизации выполнения потоков, но и как материал для построения специфических средств планирования и синхронизации для конкретных задач. Мы уже говорили, что семафор образует самодостаточный базис, позволяющий строить гораздо более сложные средства синхронизации без привлечения других средств синхронизации. В принципе это так, но нет ничего плохого и в смешанном использовании как семафоров, так и мьютексов для построения собственных средств синхронизации.
Проиллюстрируем все вышесказанное на двух примерах. Сначала мы построим «очередь сообщений», предназначенную для трансляции сообщений графической системы к «медленному» обработчику реакций. Это одно из решений весьма распространенной задачи о предотвращении «зависания» пользовательского интерфейса на период выполнения медленного обработчика. Для решения этой задачи обработчик события оконной системы (например, нажатия кнопки или выбора пункта меню) и функция, которая непосредственно производит требуемые действия (предусмотренные по наступлению указанного события - нажатия кнопки), должны располагаться в разных потоках.
Было бы удобно, если бы при поступлении новых данных от графической системы поток обработки автоматически (неявно) разблокировался и немедленно приступал к обработке, а в периоды отсутствия таких данных - простаивал в блокированном состоянии. Для реализации такой схемы мы построили синхронизирующую очередь сообщений, которая использует семафор для уведомления потока обработки о наличии новых данных. В принципе указанная задача сводится к уже упоминавшемуся ранее классу задач о синхронизации производителя и потребителя данных.
class event {
/* класс синхронизирующего события, доставляющего
уведомление о добавлении нового элемента в буфер */
public:
event() { sem_init(&_block, 0, 0); }
~event() { sem_destroy(&_block); }
void wait() { sem_wait(&_block); }
void reset() { sem_post(&_block); }
private:
sem_t _block;
};
/* шаблонный класс очереди данных */