Было бы удобно, если бы при поступлении новых данных от графической системы поток обработки автоматически (неявно) разблокировался и немедленно приступал к обработке, а в периоды отсутствия таких данных - простаивал в блокированном состоянии. Для реализации такой схемы мы построили синхронизирующую очередь сообщений, которая использует семафор для уведомления потока обработки о наличии новых данных. В принципе указанная задача сводится к уже упоминавшемуся ранее классу задач о синхронизации производителя и потребителя данных.
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;
};
/* шаблонный класс очереди данных */
template <class T> class CDataQueue {
public:
CDataQueue() {}
~CDataQueue() {}
void push(T _new_data) {
_data_queue.push(_new_data);
data_event.reset();
}
T pop() {
data_event.wait();
T res = _data_queue.front();
_data_queue.pop();
return res;
}
private:
std::queue<T> _data_queue;
event data_event;
};
Принцип работы
CDataQueue
pop()
push()
Как видите, в построении специфических средств синхронизации нет ничего сложного, вопреки часто встречающемуся утверждению, что создание средств синхронизации со специфическим поведением неадекватно трудоемко, а простейший код позволяет адаптировать возможности тривиального семафора под конкретную задачу.
А теперь хотелось бы обратить ваше внимание на тот факт, что «безопасным» использованием описанной схемы будет только вариант двух потоков — одного производителя и одного потребителя. Если несколько (более двух) потоков одновременно попробуют выполнить функции
pop()
push()
Мьютекс
Мьютекс (от mutual exclusion — взаимное исключение) — это один из базовых примитивов синхронизации QNX Neutrino. Этот элемент реализуется на уровне микроядра системы и имеет широкий набор атрибутов и настроек. Назначение мьютекса — защита участка кода от совместного выполнения несколькими потоками. Такой участок кода иногда называют критической секцией, и обычно он является областью модификации общих переменных или обращения к разделяемому ресурсу.
Принцип работы мьютекса заключается в следующем: при обращении потока к функции блокировки (захвата)
pthread_mutex_lock()
Когда действия, которые нельзя производить совместно, закончены, поток должен вызвать функцию разблокировки (освобождения)
pthread_mutex_unlock()
В QNX Neutrino 6.2.1 мьютекс имеет наибольшие возможности по тонкой настройке своих параметров среди всех иных элементов синхронизации. В связи с этим поведение мьютекса очень сильно зависит от того, какие значения вы присвоите его атрибутам.
Как видите, главное отличие мьютекса от семафора заключается в том, что он хранит информацию о потоке, исполняющем код критической секции. Отсюда и важнейшие свойства мьютекса. Мьютекс нельзя разблокировать из другого потока. Если поток захватил мьютекс, то только он может его «отпустить». Используя информацию о владельце (tid потока), система может изменять в нужное время приоритет владельца для разрешения проблемы инверсии приоритетов. Наконец, зная идентификатор потока, мьютекс может выделить ситуацию, когда поток, уже захвативший мьютекс, пытается захватить его повторно (одна из разновидностей deadlock — мертвой блокировки, когда не существует ни одного потока, способного отпустить мьютекс и разблокировать потоки, ожидающие освобождения этого мьютекса).
В ОС QNX возможен вариант работы мьютекса, не предусмотренный стандартом POSIX, — рекурсивный мьютекс. В этом режиме поток, владеющий мьютексом при повторном его захвате, не блокируется. Мьютекс только отмечает в своем внутреннем счетчике, сколько раз он был захвачен, и разблокируется только после равного количества освобождений (естественно, тем же потоком).
Все объявления относительно мьютексов находятся в заголовочном файле
<pthread.h>
#include <pthread.h>
Параметры мьютекса
Параметры мьютекса хранятся в структуре
pthread_mutexattr_t
sync_attr_t