Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Метод_ПСРВ.doc
Скачиваний:
32
Добавлен:
12.02.2016
Размер:
1.6 Mб
Скачать

3. Содержание отчёта

  1. Номер, название и цель работы.

  2. Краткое описание средств запуска процессов и механизмов канального взаимодействия.

  3. Демонстрация работы мультипроцессного приложения.

  4. Оформление письменного отчета по результатам выполнения работы.

Практическое занятие №17 Организация потоков в qnx

Цель работы – ознакомиться со способами организации дочерних потоков в QNX.

1. Краткие теоретические сведения

Для того чтобы увидеть, как «выглядит» многопоточная программа, можно запустить из командного интерпретатора команду 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) типа (обозначенной как «г» после цифры 10). Все три процесса находятся в состоянии готовности (READY), т. е. готовы использовать процессор, но в настоящее время не выполняются (поскольку в данный момент выполняется другой поток с более высоким приоритетом).

1.1. Запуск потока

Любой поток может создать другой поток в том же самом процессе; на это не налагается никаких ограничений (за исключением объема памяти, конечно!) Наиболее общий путь реализации этого – использование вызова функций pthread_create():

#include <pthread.h>

int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

Функция pthread_create() имеет четыре аргумента :

  • thread - указатель на pthread_t, где хранится идентификатор потока;

  • attr - атрибутная запись;

  • start_routine - подпрограмма, с которой начинается поток;

  • arg - параметр, который передается подпрограмме start_routine.

Отметим, что указатель thread и атрибутная запись (attr) — необязательные элементы, вы можете передавать вместо них NULL.

Параметр thread может использоваться для хранения идентификатора вновь создаваемого потока. Если, мы в этом аргументе передадим NULL, обозначив этим, что мы не заботимся о том, какой идентификатор будет иметь вновь создаваемый поток.

Если мы хотим сохранить идентификатор потока то необходимо сделать так:

pthread_t tid;

pthread_create (&tid, ...

printf («Новый поток имеет идентификатор %d\n», tid);

Такое применение совершенно типично, потому что вам часто может потребоваться знать, какой поток выполняет какой участок кода.

1.2. Администрирование атрибутов потока

Перед использованием атрибутной записи для ее инициализации следует вызвать функцию pthread_attr_init():

pthread_attr_t attr;

pthread_attr_init (&attr);

Атрибут потока «flags» (флаги)

Поток может быть создан как «синхронизирующий» («joinable») или как «обособленный» (detached), поток может наследовать атрибуты диспетчеризации от создающего потока или атрибуты диспетчеризации могут задаваться.

Для создания «синхронизирующего» потока (это значит, что с завершением этого потока можно синхронизировать другой поток) используется вызов:

pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE); (по умолчанию)

По умолчанию поток создается с атрибутом JOINABLE (присоединенный к родительскому).

Чтобы создать обособленный поток, синхронизация с завершением которого невозможна надо сделать так:

pthread_attr_setdetachstate (fcattr, THREAD_CREATE_DETACHED);

Если поток создан с атрибутом DETACHED, то, с одной стороны, он не сможет возвращать данные, а с другой – не будет уничтожен ядром при за­вершении его родителя.

Для того, чтобы поток унаследовал атрибуты диспетчеризации от потока, его создающего (то есть имел бы ту же самую дисциплину диспетчеризации и тот же самый приоритет, что и родитель), вам следует сделать так:

pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED); (по умолчанию)

Для создания потока, который использует атрибуты диспетчеризации, указанные в непосредственно в атрибутной записи вызов выглядел бы следующим образом:

pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);

При этом атрибуты предварительно устанавливаются при помощи функций pthread_attr_setschedparam() и pthread_attr_setschedpolicy().

Атрибуты потока - диспетчеризация «scheduling»

Если дисциплина диспетчеризации не наследуется, то есть вы использовали функцию pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED); то вам необходимо определить дисциплину диспетчеризации и приоритет для потока, который вы намерены создать.

int pthread_attr_setschedpolicy ( pthread_attr_t *attr, int policy);

Параметр policy - это либо SCHED_FIFO, либо SCHED_RR, либо SCHED_OTHER, то есть дисциплина диспетчеризации: FIFO, карусельная диспетчеризация, спорадическая диспетчеризация.

param — структура, которая содержит единственный элемент: sched_priority. Этот параметр можно задать путем прямого присвоения ему значения желаемого приоритета.

Теперь рассмотрим ряд примеров. Будем считать, что в обсуждаемой программе подключены нужные заголовочные файлы (<pthread.h> и <sched.h>), а также что поток, который предстоит создать, называется 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);

Самый простой метод синхронизации — это «присоединение» (joining) потоков. Реально это действие означает ожидание завершения.

Присоединение выполняется одним потоком, ждущим завершения другого потока. Ждущий поток вызывает pthreadjoin():

#include <pthread.h>

int

pthread_join (pthread_t thread, void **value_ptr);

Функции pthreadjoin() передается идентификатор потока, к которому вы желаете присоединиться, а также необязательный аргумент value_ptr, который может быть использован для сохранения возвращаемого присоединяемым потоком значения. (Вы можете передать вместо этого параметра NULL).

Где нам брать идентификатор потока?

В функции pthread_create() в качестве первого аргумента указатель на pthread_t. Там и будет сохранен идентификатор вновь созданного потока.