Идентификатором потока (значимым только внутри одного процесса!) является TID (Thread ID), присваиваемый потоку при его создании вызовом
pthread_create()
. TID позволяет процессу (а также системе в рамках процесса) однозначно идентифицировать каждый поток. Нумерация TID в QNX начинается с 1 (это всегда главный поток процесса, порожденный
main()
) и последовательно возрастает по мере создания потоков (до 32767).
[17]
Еще одним важнейшим атрибутом потока является приоритет его выполнения. Для каждого из уровней приоритетов, обслуживаемых системой (в QNX 6.2.1 таких уровней 64, в QNX 6.3 — 256), поддерживается циклическаяочередь потоков, готовых к исполнению (на деле большая часть из таких очередей оказывается пустой). Все политики диспетчеризации работают только с потоками из одной такой очереди: очереди потоков наивысшего из присутствующих в системе приоритетов. Если в системе выполняется поток высокого приоритета, то ни один поток более низкого приоритета не получит управление до тех пор, пока поток высокого приоритета не будет переведен в блокированное состояние в ожидании некоторого события (рис. 2.3).
Рис. 2.3. Диспетчеризация потоков с различными приоритетами
На рис. 2.3 представлены два процесса, каждый из которых создает внутри себя несколько потоков, но на этот раз различных приоритетов (10 и 12). Жирной пунктирной линией показан порядок, в котором потоки высокого приоритета (12) объединены в циклическую очередь диспетчеризации. Это активная очередь диспетчеризации (наивысшего приоритета). Тонкой линией показан порядок потоков в другой очереди (приоритета 10). До тех пор пока все потоки активной очереди не окажутся в силу каких-либо обстоятельств в блокированном состоянии, ни один из потоков очереди приоритета 10 не получит ни единого кванта времени.
Создание нового потока
Создание нового потока в программном коде осуществляет вызов:
int pthread_create(pthread_t* thread,
const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg);
где
thread
—
NULL
или указатель переменной типа
pthread_t
, значение которой будет загружено идентификатором созданного потока после успешного выполнения функции. Далее это значение (это и есть TID) может использоваться по тексту программы для идентификации созданного потока.
attr
—
NULL
или указатель структуры типа
pthread_attr_t
. Если это значение
NULL
, то созданный поток будет иметь набор параметров, устанавливаемых по умолчанию. Если нет, то поток будет создан с параметрами, установленными в структуре
attr
. Модификация полей
attr
после создания потока (то есть после вызова функции) не оказывает никакого эффекта на параметры потока, и вообще говоря, структура
attr
может быть уничтожена сразу же после вызова
pthread_create()
. Документация предостерегает от прямой манипуляции значениями полей этой структуры, предлагая использовать для этого функции
pthread_attr_init()
и
pthread_attr_set_*()
.
start_routine
— функция типа
void*()(void*)
, уже упоминавшаяся выше как функция потока; это тот код, который будет фактически выполняться в качестве отдельного потока. Если выполнение этой функции завершается по
return
, то происходит нормальное завершение потока с вызовом
pthread_exit()
, использующим значение, возвращаемое start_routine в качестве
статуса завершения. (Исключением является поток, связанный с
main()
; он при завершении выполняет вызов
exit()
.)
arg
— указатель на блок данных, передаваемых
start_routine
в качестве входного параметра. Этот параметр подробно рассмотрен далее.
Чаще всего (однако совершенно необязательно) функция потока
start_routine
представляет собой бесконечный цикл, в котором выполняются некоторые действия с выходом из цикла в том случае, когда нужно завершить выполнение и уничтожить созданный поток. Выглядит это следующим образом:
// функция потока:
void* ThreadProc(void* data) {
while (true) {
// ... выполняется работа ...
if (...) break;
// после этого поток нам уже не нужен!
}
return NULL;
}