Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
Шрифт:
Давайте перейдем теперь к потоку-«производителю» и рассмотрим, как он использует библиотеку ждущих блокировок. Вот его полная реализация:
Как вы видите, поток-«производитель» также блокирует мутекс, чтобы получить монопольный доступ к флагу data_ready перед его установкой.
Давайте рассмотрим происходящее в подробностях. Определим состояния «потребителя» и «производителя» следующим образом:
| Состояние | Означает |
|---|---|
| CONDVAR | ожидание соответствующей ждущей блокировке условной переменной |
| MUTEX | ожидание мутекса |
| READY | состояние готовности, т.е., готов выполняться или уже выполняется |
| INTERRUPT | ожидание прерывания от аппаратных средств |
| Действие | Владелец мутекса | Состояние «потребителя» | Состояние «производителя» |
|---|---|---|---|
| «потребитель» блокирует мутекс | «потребитель» | READY | INTERRUPT |
| «потребитель» проверяет флаг data_ready | «потребитель» | READY | INTERRUPT |
| потребитель вызывает функцию pthread_sleepon_wait | «потребитель» | READY | INTERRUPT |
| функция pthread_sleepon_wait разблокирует мутекс | мутекс свободен | READY | INTERRUPT |
| функция pthread_sleepon_wait блокируется | мутекс свободен | CONDVAR | INTERRUPT |
| пауза до прерывания | мутекс свободен | CONDVAR | INTERRUPT |
| аппаратные средства генерируют данные | мутекс свободен | CONDVAR | READY |
| «производитель» блокирует мутекс | «производитель» | CONDVAR | READY |
| «производитель» устанавливает флаг data_ready | «производитель» | CONDVAR | READY |
| «производитель» вызывает pthread_sleepon_signal | «производитель» | CONDVAR | READY |
| «потребитель» «пробуждается», функция pthread_sleepon_wait пытается заблокировать мутекс | «производитель» | MUTEX | READY |
| «производитель» разблокирует мутекс | мутекс свободен | MUTEX | READY |
| «потребитель» получает мутекс | «потребитель» | READY | READY |
| «потребитель» обрабатывает данные | «потребитель» | READY | READY |
| «производитель» ждет новых данных от аппаратуры | «потребитель» | READY | INTERRUPT |
| пауза («потребитель» обрабатывает полученные данные) | «потребитель» | READY | INTERRUPT |
| «потребитель» завершает обработку и разблокирует мутекс | мутекс свободен | READY | INTERRUPT |
| «потребитель» возвращается в начало цикла и блокирует мутекс | «потребитель» | READY | INTERRUPT |
Последняя строка в таблице повторяет первую — мы совершили один полный цикл.
Каково назначение флага data_ready? Он служит для двух целей:
• Он является флагом состояния — посредником между «потребителем» и «производителем», указывающим на состояние системы. Если флаг установлен в состояние 1, это означает, что данные доступны для обработки; если этот флаг установлено в состояние 0, это означает, что данных нет, и поток-потребитель должен быть заблокирован.
• Он выполняет функцию «места, где происходит синхронизация со ждущей блокировкой». Более формально говоря, адрес переменной data_ready используется как уникальный идентификатор объекта, по которому осуществляется ждущая блокировка. Мы запросто могли бы применить «
• К обсуждению различий между функциями pthread_sleepon_signal и pthread_sleepon_broadcast мы еще вернемся в разговоре об условных переменных.
Условные переменные
Условные переменные (или «condvars») очень похожи на ждущие блокировки, которые мы рассматривали выше. В действительности, ждущие блокировки — это надстройка над механизмом условных переменных, и именно поэтому в таблице, иллюстрировавшей использование ждущих блокировок, у нас встречалось состояние CONDVAR. Функция pthread_cond_wait точно так же освобождает мутекс, ждет, а затем повторно блокирует мутекс, аналогично функции pthread_sleepon_wait.
Давайте опустим вступление и обратимся к нашему примеру о «производителе» и «потребителе» из раздела о ждущих блокировках, но вместо ждущих блокировок будем использовать условные переменные. А затем уже обсудим вызовы.
Этот пример в значительной степени похож на программу с применением ждущей блокировки, с небольшими отличиями (мы добавили несколько вызовов printf, а также функцию main, чтобы программа могла работать!) Первое отличие, которое бросается в глаза, — здесь использован новый тип данных,