Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform | страница 53



и pthread_sleepon_unlock(). Давайте модифицируем наш поток-«потребитель»:

>consumer() {

> while (1) {

>  pthread_sleepon_lock();

>  while (!data_ready) {

>   // WAIT

>  }

>  // Обработать данные

>  data_ready = 0;

>  pthread_sleepon_unlock();

> }

>}

Здесь мы добавили «потребителю» установку и снятие блокировки. Это означает, что потребитель может теперь надежно проверять флаг data_ready, не опасаясь гонок, а также надежно его устанавливать.

Великолепно! А как насчет собственно процесса ожидания? Как мы и предполагали ранее, там действительно применяется вызов функции pthread_sleepon_wait(). Вот второй while-цикл:

>while (!data_ready) {

> pthread_sleepon_wait(&data_ready);

>}

Функция pthread_sleepon_wait() в действительности выполняет три действия:

1. Разблокирует мутекс библиотеки ждущих блокировок.

2. Выполняет собственно операцию ожидания.

3. Снова блокирует мутекс библиотеки ждущих блокировок.

Причина обязательной разблокировки/блокировки мутекса библиотеки проста: поскольку суть мутекса состоит в обеспечении взаимного исключения доступа к флагу data_ready, мы хотим запретить потоку-«производителю» изменять флаг data_ready, пока мы его проверяем. Но если мы не разблокируем флаг впоследствии, то поток-«производитель» не сможет его установить, чтобы сообщить нам о доступности данных! Операция повторной блокировки выполняется автоматически исключительно для удобства, чтобы вызвавший функцию pthread_sleepon_wait() поток не беспокоился о состоянии блокировки после «пробуждения».

Давайте перейдем теперь к потоку-«производителю» и рассмотрим, как он использует библиотеку ждущих блокировок. Вот его полная реализация:

>producer() {

> while (1) {

>  // Ждать прерывания от оборудования...

>  pthread_sleepon_lock();

>  data_ready = 1;

>  pthread_sleepon_signal(&data_ready);

>  pthread_sleepon_unlock();

> }

>}

Как вы видите, поток-«производитель» также блокирует мутекс, чтобы получить монопольный доступ к флагу data_ready перед его установкой.

Клиента «пробуждает» не установка флага data_ready в единицу (1), а вызов функции pthread_sleepon_signal()!

Давайте рассмотрим происходящее в подробностях. Определим состояния «потребителя» и «производителя» следующим образом:

СостояниеОзначает
CONDVARожидание соответствующей ждущей блокировке условной переменной
MUTEXожидание мутекса
READYсостояние готовности, т.е., готов выполняться или уже выполняется
INTERRUPTожидание прерывания от аппаратных средств
ДействиеВладелец мутексаСостояние «потребителя»