Атрибуты потока «scheduling» (диспетчеризация)
Наконец, если вы определяете PTHREAD_EXPLICIT_SCHED для функции pthread_attr_setinheritsched() , тогда вам необходимо будет как-то определить дисциплину диспетчеризации и приоритет для потока, который вы намерены создать.
Это выполняется с помощью двух функций:
int pthread_attr_setschedparam(pthread_attr_t *attr,
const struct sched_param *param);
int pthread_attr_setschedpolicy(pthread_attr_t *attr,
int policy);
С параметром policy все просто — это либо SCHED_FIFO, либо SCHED_RR, либо SCHED_OTHER.
В рассматриваемой версии QNX/Neutrino параметр SCHED_OTHER интерпретируется как SCHED_RR (карусельная диспетчеризация).
Параметр param — структура, которая содержит единственный элемент: sched_priority . Задайте этот параметр путем прямого присвоения ему значения желаемого приоритета.
Стандартная ошибка, которой следует избегать, заключается в задании PTHREAD_EXPLICIT_SCHED и затем определением только дисциплины диспетчеризации. Проблема состоит в том, что в инициализированной атрибутной записи значение param.sched_priority есть 0 (ноль). Это тот же самый приоритет, что и у «холостого» потока (IDLE), что означает, что создаваемый вами поток будет конкурировать за процессор с «холостым» потоком.
Плавали, знаем. :-)
На том, что QSSL зарезервировала нулевой приоритет только для «холостого» потока, уже «прокололось» немало программистов. Поток с нулевым приоритетом просто не сможет выполняться.
Несколько примеров
Давайте рассмотрим ряд примеров. Будем считать, что в обсуждаемой программе подключены нужные заголовочные файлы ( и ), а также что поток, который предстоит создать, называется new_thread() , и для него существуют все необходимые прототипы и определения.
Самый обычный способ создания потока — просто оставить везде значения по умолчанию:
pthread_create(NULL, NULL, new_thread , NULL);
В вышеупомянутом примере мы создали наш новый поток со значениями параметров по умолчанию и передали ему NULL в качестве его единственного параметра (третий NULL в указанном выше вызове pthread_create() ).
Вообще говоря, вы можете передавать вашему новому потоку что угодно через параметр arg . Например, число 123:
pthread_create(NULL, NULL, new_thread , (void*)123);
Более сложный пример — создание «обособленного» (detached) потока с диспетчеризацией карусельного типа (RR) и приоритетом 15:
pthread_attr_t attr;
// Инициализировать атрибутную запись
pthread_attr_init(&attr);
// Установить «обособленность»
pthread_attr_setdetachstate(&attr,
PTHREAD_CREATE_DETACHED);
// Отменить наследование по умолчанию (INHERIT_SCHED)
pthread_attr_setinheritsched(&attr,
PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
attr.param.sched_priority = 15;
// И, наконец, создать поток
pthread_create(NULL, &attr, new_thread, NULL);
Для того чтобы увидеть, как «выглядит» многопоточная программа, можно запустить из командного интерпретатора команду pidin
. Скажем, нашу программу зовут spud
. Если мы выполняем pidin
один раз до создания программой spud потоков, и еще раз — после того, как spud
создала два потока (тогда всего их будет три), то вот как примерно будет выглядеть вывод (я укоротил вывод pidin
для демонстрации только того, что относится к spud
):
# pidin
pid tid name prio STATE Blocked
12301 1 spud 10r READY
# pidin
pid tid name prio STATE Blocked
12301 1 spud 10r READY
12301 2 spud 10r READY
12301 3 spud 10r READY
Вы можете видеть, что процесс spud
(идентификатор процесса 12301) имеет три потока (столбец «tid» в таблице). Эти три поток» выполняются с приоритетом 10, с диспетчеризацией карусельного (RR) типа (обозначенной как «r» после цифры 10). Все три процесса находятся в состоянии готовности (READY), т. е. готовы использовать процессор, но в настоящее время не выполняются (поскольку в данный момент выполняется другой поток с более высоким приоритетом).
Теперь, когда мы знаем все о создании потоков, давайте рассмотрим, как и где мы можем этим воспользоваться.
Где хороша многопоточность
Существует два класса задач, где можно было бы эффективно применять многопоточность.
Потоки подобны перегруженным операторам в языке Си++. Поначалу может показаться хорошей идеей перегрузить каждый оператор какой-либо дополнительной интересной функцией, но это сделает программу трудной для восприятия. Аналогичная ситуация с потоками. Вы могли бы создать множество потоков, но это усложнит ваш код и сделает программу малопонятной, а значит, сложной в сопровождении. Разумное же применение потоков, наоборот, внесет в программу дополнительную функциональную ясность.
Читать дальше