Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
Шрифт:
Итак, если вы установили область разделяемой памяти между двумя процессами и разместили в ней мутекс, ничто не мешает вам с его помощью синхронизировать потоки в двух (или более!) процессах — функции pthread_mutex_lock и pthread_mutex_unlock будут работать точно так же.
Пулы потоков
Другое существенное дополнение в QNX/Neutrino — это понятие пула потоков. Вы будете часто обращать внимание в ваших программах на то обстоятельство, что вам хотелось бы иметь несколько потоков и управлять их поведением в определенных пределах. Например, для сервера вы можете решить, что первоначально в ожидании сообщения от клиента должен быть блокирован только один поток. Когда этот поток получит сообщение и пойдет обслуживать запрос, вы можете принять решение о том, что хорошо было бы создать другой поток и блокировать его в ожидании на случай поступления другого запроса — тогда этот запрос будет кому обработать. И так далее.
Подобные операции в жизни — обычное дело, и для задач такого рода QNX/Neutrino предоставляет для этого специальную библиотеку.
В рамках данного обсуждения важно понять, что следует различать два режима потоков в пулах:
• режим блокирования;
• режим обработки.
В режиме блокирования поток обычно вообще не использует ресурсы процессора. В типовом сервере это соответствует ситуации, когда поток ждет сообщения. Противоположностью этого режима является режим обработки, в котором поток может как использовать, так и не использовать ресурсы процессора — это зависит от структуры процесса. Чуть позже мы рассмотрим функции работы с пулами потоков, и вы увидите, что они дают возможность управлять количеством как блокированных, так и обрабатывающих потоков.
Для работы с пулами потоков в QNX/Neutrino предусмотрены следующие функции:
Как видно из имен функций, вы в первую очередь создаете пул потоков, используя функцию thread_pool_create, а затем запускаете этот пул при помощи функции thread_pool_start. Когда вы закончили свои дела с пулом потоков, вы можете использовать функцию thread_pool_destroy для его уничтожения. Заметьте, что функция thread_pool_destroy может вам вообще не понадобиться — например, когда ваша программа суть сервер, который работает «вечно».
Итак, первая функция, на которую следует обратить внимание — это функция thread_pool_create. У нее два параметра: attr и flags. Параметр attr — атрибутная запись, которая определяет рабочие параметры пула потоков (см.
Я разбил определение типа
Сначала проанализируем «параметры пула потоков», чтобы понять, как можно управлять числом потоков в пуле и их атрибутами. Имейте в виду, что здесь мы будем говорить
Приведенный ниже рисунок иллюстрирует связи между параметрами lo_water, hi_water и maximum.
Жизненный цикл потока в пуле потоков.
(Заметьте, что как «CA» здесь обозначается функция context_alloc, как «CF» — функция context_free, как «режим блокирования» — функция block_func, а как «режим обработки» — функция handler_func.
attr | Это атрибутная запись, которая применяется при создании потока. Мы уже обсуждали эту структуру ранее (в разделе «Атрибутная запись потока»). Вспомните — это та самая структура, которая задает характеристики нового потока: приоритет, размер стека, и т.д. |
lo_water | (От «Low watermark», буквально — «нижняя ватерлиния» — прим. ред.) Этот параметр задает минимальное количество потоков, которые должны находиться в режиме блокирования. В типовом сервере это было бы количество потоков, например, ждущих запроса. Если число ждущих потоков меньше, чем значение параметра lo_water, (например, потому что мы только что приняли сообщение, и один из ждущих потоков переключился на его обработку), тогда создается дополнительно еще increment потоков. Это представлено на рисунке в виде первого этапа, обозначенного как «создание потока». |
increment | (Буквально — «приращение» — прим. ред.) Этот параметр определяет, сколько потоков должны быть созданы сразу, если число потоков, находящихся в режиме блокирования, становится меньше значения параметра lo_water. В выборе значения для этого параметра вы бы наиболее вероятно начали со значения 1 (единица). Это означало бы, что если бы число потоков в режиме блокирования стало бы меньше значения параметра lo_water, то пулом потоков был бы создан дополнительно ровно один поток. Для более тонкой настройки параметра increment можно понаблюдать за поведением процесса и определить, может ли этому параметру понадобиться принимать значения, отличные от единицы. Например, если ваш процесс периодически получает «всплески» запросов, то из того, что число потоков, находящихся в режиме блокирования, упало ниже значения lo_water, можно было бы сделать вывод как раз о таком «всплеске» и принять решение о создании более чем одного резервного потока. |
hi_water | (От «high watermark», буквально — «верхняя ватерлиния» — прим. ред.) Этот параметр указывает верхний предел числа потоков, которые могут быть в режиме блокирования одновременно. По мере завершения своих операций по обработке данных, потоки обычно будут возвращаться в режим блокирования. Однако, у библиотеки поддержки пулов потоков есть внутренний счетчик числа потоков, находящихся в режиме блокирования, и если его значение превышает значение параметра hi_water, библиотека автоматически уничтожит поток, который вызвал переполнение (то есть тот поток, который только что завершил обработку и намеревался возвратиться в режим блокирования). Это показано на рисунке раздвоением стрелки, исходящей из блока «режим обработки» — одна стрелка ведет к «режиму блокирования», а вторая — к блоку операции «CF» и далее на уничтожение потока. Таким образом, сочетание параметров lo_water и hi_water позволяет вам четко определять диапазон числа потоков, одновременно находящихся в режиме блокирования. |
maximum | Параметр указывает на максимальное число потоков, которые вообще могут работать одновременно в результате действий библиотеки поддержки пулов потоков. Например, при создании новых потоков в случае их нехватки (когда число блокированных потоков падает ниже границы lo_water) общее количество потоков было бы ограничено параметром maximum. |
Другой ключевой параметр, предназначенный для управления потоками, — это параметр flags, передаваемый функции thread_pool_create. Он может принимать одно из следующих значений:
POOL_FLAG_EXIT_SELF
Не делать возврат из функции thread_pool_start и не включать вызывающий поток в пул.
POOL_FLAG_USE_SELF
Не делать возврат из функции thread_pool_start, но включить вызывающий поток в пул.