Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Параллельное программирование над общей памятью. POSIX Threads. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
1 Мб
Скачать

start_routine – функция, которую будет исполнять создаваемый поток;

arg – единственный аргумент, который будет передан в функцию start_routine; если в аргументе нет необходимости, необходимо указывать NULL.

Сообщения об ошибках:

ноль – ошибки отсутствуют;

EAGAIN – недостаточно ресурсов для создания потока либо превышен лимит максимального количества потоков в процессе;

EPERM – недостаточно прав доступа, чтобы установить требуемые параметры планирования или политику планирования (подробнее

см. раздел 3).

Созданный поток может также создавать новые потоки. При этом между потоками нет какой-либо иерархии или зависимостей по умолчанию.

Завершить работу потока позволяет функция pthread_exit(). Она также позволяет передать некоторое значение через область памяти, на которую указывает единственный параметр функции. Например, таким образом можно передать статус завершения потока.

Прототип функции:

void pthread_exit(void *value_ptr);

Параметры функции:

value_ptr – указатель на значение, которое необходимо передать (нельзя передавать указатель на локальную переменную потока, так как она будет уничтожена при завершении работы потока), ли-

бо NULL.

Сообщения об ошибках:

отсутствуют.

В случае, если основной поток заканчивает свою работу, все пото-

ки будут принудительно завершены. Вызов функции pthread_exit() в конце функции main() позволяет дождаться завершения работы всех потоков прежде, чем завершится работа программы.

Пример 1 иллюстрирует создание нового потока, который выводит сообщение на экран.

11

Пример 1. Создание потока

#include <cstdlib> #include <iostream> #include <cstring> #include <pthread.h>

using namespace std;

/* Функция, которую будет исполнять созданный поток */ void *thread_job(void *arg)

{

cout << "Thread is running..." << endl;

}

int main()

{

//Определяем переменные: идентификатор потока и код ошибки pthread_t thread;

int err;

//Создаём поток

err = pthread_create(&thread, NULL, thread_job, NULL);

//Если при создании потока произошла ошибка, выводим

//сообщение об ошибке и прекращаем работу программы if(err != 0) {

cout << "Cannot create a thread: " << strerror(err) << endl; exit(-1);

}

//Ожидаем завершения созданного потока перед завершением

//работы программы

pthread_exit(NULL);

}

АТРИБУТЫ ПОТОКОВ

Атрибуты потока позволяют менять его свойства. Они определяются только во время создания потока и не могут быть изменены позднее.

12

При необходимости переопределить атрибуты потока, используемые по умолчанию, необходимо объявить переменную типа pthread_attr_t, которая будет содержать сведения о значениях атрибутов потока, инициализировать ее с помощью функции pthread_attr_ init(), изменить значения необходимых атрибутов с помощью соответствующих функций и передать ее в качестве параметра в функцию pthread_create(). Одну и ту же переменную можно использовать для создания нескольких потоков. При этом, если изменить переменную (изменить значение одного из атрибутов) после создания потока, на нем это никак не отразится. Когда переменная больше не нужна (например, все необходимые потоки созданы, либо программа завершается), связанную с ней информацию необходимо удалить с помощью функции pthread_attr_destroy().

Прототипы функций:

int pthread_attr_init(pthread_attr_t *attr);

int pthread_attr_destroy(pthread_attr_t *attr);

Параметры функций:

attr – указатель на атрибуты потока.

Сообщения об ошибках:

ноль – ошибки отсутствуют. pthread_attr_init():

ENOMEM – при инициализации нового объекта attr не хватило памяти.

Скаждым атрибутом связаны две функции: одна позволяет изменять значения атрибута, а вторая – прочитать его значение. Основные функции2 для работы с атрибутами:

Прототипы функций:

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

2 Дополнительные атрибуты, относящиеся к планированию потоков, и функции для работы с ними рассмотрены в разделе 3.

13

int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);

int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize);

int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);

int pthread_attr_getstack(const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize);

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize);

Параметры функций:

attr – указатель на атрибуты потока;

остальные параметры задают значения атрибутов и описаны в табл. 2.

 

 

Таблица 2

 

Перечень атрибутов потока

 

 

 

Атрибут

Возможные значения

Описание

 

 

 

detachstate

PTHREAD_CREATE_DETACHED

Определяет тип создавае-

 

PTHREAD_CREATE_JOINABLE

мого потока: присоединя-

 

 

емый или отсоединенный

 

 

(см. ниже)

 

 

 

guardsize

Размер в байтах

Задает размер охранной

 

 

зоны для защиты от пере-

 

 

полнения стека (выделяет-

 

 

ся в памяти сразу за сте-

 

 

ком)

 

 

 

stackaddr

Адрес в байтах

Задает адрес стека

 

 

 

stacksize

Размер в байтах

Задает размер стека

 

 

 

14

Сообщения об ошибках:

ноль – ошибки отсутствуют;

EINVAL – неверное значение атрибута.

Программа из примера 2 создает новый поток, который получит нестандартный размер стека.

Пример 2. Изменение атрибутов потока

#include <cstdlib> #include <iostream> #include <cstring> #include <pthread.h>

using namespace std;

/* Функция, которую будет исполнять созданный поток */ void *thread_job(void *arg)

{

cout << "Thread is running..." << endl;

}

int main()

{

// Определяем переменные pthread_t thread; pthread_attr_t thread_attr; int err;

//Идентификатор потока

//Атрибуты потока

//Код ошибки

//Инициализируем переменную для хранения атрибутов потока err = pthread_attr_init(&thread_attr);

if(err != 0) {

cout << "Cannot create thread attribute: " << strerror(err) << endl; exit(-1);

}

//Устанавливаем минимальный размер стека для потока (в байтах) err = pthread_attr_setstacksize(&thread_attr, 5*1024*1024);

if(err != 0) {

cout << "Setting stack size attribute failed: " << strerror(err)

<<endl;

exit(-1);

}

15

// Создаём поток

err = pthread_create(&thread, &thread_attr, thread_job, NULL); if(err != 0) {

cout << "Cannot create a thread: " << strerror(err) << endl; exit(-1);

}

// Освобождаем память, занятую под хранение атрибутов потока pthread_attr_destroy(&thread_attr);

//

pthread_exit(NULL);

}

ПРИСОЕДИНЯЕМЫЕ И ОТСОЕДИНЕННЫЕ ПОТОКИ

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

Тип создаваемого потока определяется атрибутом detachstate: если он установлен в значение PTHREAD_CREATE_DETACHED, будет создан отсоединенный поток; если же его значение равно PTHREAD_CREATE_JOINABLE – будет создан присоединяемый поток. По умолчанию поток создается присоединяемым.

Для присоединения потока используется функция pthread_ join(). Она приостанавливает выполнение текущего потока до момента завершения присоединяемого потока, идентификатор которого указан в качестве параметра. Из присоединяемого потока имеется возможность получить возвращаемое значение.

Прототип функции:

int pthread_join(pthread_t thread, void **value_ptr);

Параметры функции:

thread – идентификатор присоединяемого потока;

value_ptr – указатель на область памяти, через которую можно получить значение, переданное функции pthread_exit() при за-

16

вершении потока; допустимо указывать NULL, если это значение не нужно.

Рис. 1. Создание потоков

Сообщения об ошибках:

ноль – ошибки отсутствуют;

ESRCH – не удается найти поток, соответствующий идентификато-

ру thread.

На рис. 1 показано 5 потоков. Функцию main() исполняет поток 1 (основной поток). Потоки 2, 3 и 4 созданы с типом PTHREAD_CREATE_ JOINABLE, а поток 5 – с типом PTHREAD_CREATE_DETACHED. Видно,

что присоединять поток можно как к его родителю (в примере – потоки 2 и 3), так и к другому потоку (поток 4).

Функция pthread_detach() позволяет изменить тип потока, изначально созданного как присоединяемый, на отсоединенный.

Прототип функции:

int pthread_detach(pthread_t thread);

17

Параметры функции:

thread – идентификатор потока, тип которого необходимо изменить на отсоединенный.

Сообщения об ошибках:

ноль – ошибки отсутствуют.

Отсоединенный поток по-прежнему работает в рамках своего про-

цесса и в случае его завершения (прекращения работы функции main()) будет также завершен. Чтобы все отсоединенные потоки корректно завершили свою работу, последней командой функции main() должен быть вызов функции pthread_exit().

ПЕРЕДАЧА ПАРАМЕТРОВ В ПОТОКИ

При создании потока имеется возможность передать в функцию потока указатель на область, содержащую параметры. Указатель необходимо привести к типу (void *) и передать последним параметром функции pthread_create().

Программа из примера 3 передает число, указанное пользователем при запуске программы, в качестве аргумента в функцию потока.

Пример 3. Передача параметра в функцию потока

#include <cstdlib> #include <iostream> #include <cstring> #include <pthread.h>

using namespace std;

void *thread_job(void *arg)

{

//Преобразуем указатель на параметр к правильному типу int *param = (int *) arg;

//Выводим параметр на экран

cout << "Thread is running... Parameter is " << *param << endl;

}

int main(int argc, char *argv[])

{

18

//Определяем переменные pthread_t thread;

int param; // Переменная для хранения параметра, передаваемого потоку int err;

if(argc != 2) {

cout << "Wrong number of arguments" << endl; exit(-1);

}

param = atoi(argv[1]);

//Создаем поток

err = pthread_create(&thread, NULL, thread_job, (void *) ¶m); if(err != 0) {

cout << "Cannot create a thread: " << strerror(err) << endl; exit(-1);

}

// Ожидаем завершения работы потока pthread_join(thread, NULL);

if(err != 0) {

cout << "Cannot join a thread: " << strerror(err) << endl; exit(-1);

}

}

ПРОЧИЕ ФУНКЦИИ

Функция pthread_self() возвращает идентификатор вызывающего потока.

Прототип функции: pthread_t pthread_self(void);

Параметры функции:

отсутствуют.

Сообщения об ошибках:

отсутствуют.

Поскольку представление идентификатора потока определяется ре-

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

19

но. Для этого служит специальная функция pthread_equal(). Она

возвращает ноль, если идентификаторы разные, и число больше нуля, если идентификаторы одинаковы.

Прототип функции:

int pthread_equal(pthread_t t1, pthread_t t2);

Параметры функции:

t1 – идентификатор первого потока;

t2 – идентификатор второго потока.

Сообщения об ошибках:

отсутствуют.

Функция pthread_once() позволяет выполнить некоторую функцию однократно любым потоком процесса. Вызывать ее могут все потоки. Первое обращение к функции pthread_once() приведет к исполнению функции init_routine(), при последующих обращениях функция init_routine() выполнена не будет.

Прототип функции:

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

Параметры функции:

once_control – синхронизирующая структура данных, которая должна быть инициализирована до обращения к функции pthread_once() значением PTHREAD_ONCE_INIT;

init_routine – функция, которая будет исполнена однократно.

Сообщения об ошибках:

ноль – ошибки отсутствуют.

ДОПОЛНИТЕЛЬНАЯ ЛИТЕРАТУРА

1.Средства параллельного программирования в ОС LINUX: учебное пособие / Р.Х. Садыхов, Л.П. Поденок, А.В. Отвагин и др. [Электронный ресурс] // OpenNET. – URL: https://www.opennet.ru/docs/RUS/linux_parallel/ (дата обращения: 06.11.2017).

20

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]