Чтение онлайн

ЖАНРЫ

Параллельное и распределенное программирование на С++
Шрифт:

// Листинг 4.1. Использование модели делегирования в

// простой многопоточной программе

#include <iostream>

#include <pthread.h>

void *task1(void *X) //define task to be executed by ThreadA

{

//...

cout << «Thread A complete» << endl;

}

void *task2(void *X) //define task to be executed by ThreadB

{

//...

cout << «Thread B complete» << endl;

}

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

{

pthread_t ThreadA,ThreadB; // declare threads

pthread_create(&ThreadA,NULL,task1,NULL); // create threads

pthread_create(&ThreadB,NULL,task2,NULL);

// additional processing

pthread_join(ThreadA,NULL); // wait for threads

pthread_join(ThreadB,NULL);

return(0);

}

В

листинге 4.1 делается акцент на определении набора инструкций для основного потока. Основным в данном случае является управляющий поток, который объявляет два рабочих потока ThreadA и ThreadB. С помощью функции pthread_create эти два потока связываются с задачами, которые они должны выполнить (taskl и task2). Здесь (ради простоты примера) эти задачи всего лишь отп равляют сообщение в стандартный выходной поток, но понятно, что они могли бы быть запрограммированы на нечто более полезное. При вызове функции pthread_create потоки немедленно приступают к выполнению назначенных им задач. Работа функции pthread_join аналогична работе функции wait для процессов. Основной поток ожидает до тех пор, пока не завершатся оба рабочих потока. Диаграмма последовательностей, соответствующая листингу 4.1, показана на рис. 4.11. Обратите внимание на то, что происходит с потоками выполнения при вызове функций pthread_create и pthread_join .

На рис.4.11 показано, что вызов функции pthread_create является причиной разветвления, или образования «вилки» в основном потоке выполнения, в результате чего образуются два дополнительных «ручейка» (по одному для каждой задачи), которые выполняются параллельно. Функция pthread_create завершается сразу же после создания потоков. Эта функция предназначена для создания асинхронных потоков. Это означает, что, как рабочие, так и основной поток, выполняют свои инструкции независимо друг от друга. Функция pthread_join заставляет основной поток ожидать до тех пор, пока все рабочие потоки завершатся и «присоединятся» к основному.

Рис. 4.11. Диаграмма последовательностей, соответствующая листингу 4.1

Компиляция и компоновка многопоточных программ

Все многопоточные программы, использующие библиотеку потоков POSIX, должны включать заголовок: < pthread.h >

Для компиляции многопоточного приложения в средах UNIX или Linux с помощью компиляторов командной строки g++ или gcc необходимо скомпоновать его с библиотекой Pthreads. Для задания библиотеки используйте опцию – l. Так, команда – lpthread обеспечит компоновку вашего приложения с библиотекой, которая согласуется с многопоточным интерфейсом, определенным стандартом POSIX 1003.1с. Библиотеку Pthread, libpthread.so , следует поместить в каталог, в котором хранится системная стандартная библиотека, обычно это /usr/lib. Если она будет находиться не в стандартном каталоге, то для того, чтобы обеспечить поиск компилятора в заданном каталоге до поиска в стандартных, используйте опцию – L. По команде g++ -о blackboard -L /src/local/lib blackboard.cpp -lpthread компилятор выполнит поиск библиотеки Pthread сначала в каталоге /src/local/lib, а затем в стандартных каталогах.

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

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

Создание потоков

Библиотека Pthreads используется для создания, поддержки и управления потоками многопоточных программ и приложений. При создании многопоточной программы потоки могут создаваться на любом этапе выполнения процесса, поскольку это — динамические образования. Функция pthread_create создает новый поток в адресном пространстве процесса. Параметр thread указывает на дескриптор, или идентификатор (id), создаваемого потока. Новый поток будет иметь атрибуты, заданные объектом attr. Созданный поток немедленно приступит к выполнению инструкций, заданных параметром start_routine с использованием аргументов, заданных параметром arg. При успешном создании потока функция возвращает его идентификатор (id), значение которого сохраняется в параметре thread.

Синопсис

#include <pthread.h>

int pthread_create(pthread_t *restrict thread,

const pthread_attr_t *restrict attr,

void *(*start_routine)(void*),

void *restrict arg) ;

Если параметр attr содержит значение NULL, новый поток будет использовать атрибуты, действующие по умолчанию. В противном случае новый поток использует атрибуты, заданные параметром attr при его создании. Если же значение параметра attr изменится после того, как поток был создан, это никак не отразится на его атрибутах. При завершении параметра-функции start_routine завершается и поток, причем так, как будто была вызвана функция pthread_exit с использованием в качестве статуса завершения значения, возвращаемого функцией start_routine.

При успешном завершении функция возвращает число 0 . В противном случае по-не создается, и функция возвращает код ошибки. Если в системе отсутствуют ресурсы для создания потока, или в процессе достигнут предел по количеству возможных потоков, выполнение функции считается неудачным. Неудачным оно также будет в случае, если атрибут потока задан некорректно или если инициатор вызова потока не имеет разрешения на установку необходимых атрибутов потока.

Приведем примеры создания двух потоков с заданными по умолчанию атрибутами:

pthread_create(&threadA,NULL, taskl,NULL) ;

pthread_create(&threadB,NULL, task2, NULL) ;

Это — два вызова функции pthread_create из листинга 4 .1. Оба потока создаются с атрибутами, действующими по умолчанию.

В программе 4 .1 отображен основной поток, который передает аргумент из командной строки в функции, выполняемые потоками.

// Программа 4.1

#include <iostream>

#include <pthread.h>

#include <stdlib.h>

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

pthread_t ThreadA,ThreadB;

int N;

if(argc != 2) {

cout << «error» << endl;

exit (1);

}

N = atoi(argv[l]);

pthread_create(&ThreadA,NULL, taskl,&N);

pthread_create(&ThreadB,NULL, task2, &N);

cout « «Ожидание присоединения потоков.» « endl;

pthread_join(ThreadA,NULL) ;

pthread_join(ThreadB,NULL);

return (0) ;

};

В программе 4 .1 показано, как основной поток может передать аргументы из командной строки в каждую из потоковых функций. Число в командной строке имеет строковый тип. Поэтому в основном потоке аргумент сначала преобразуется в целочисленное значение, и только после этого результат преобразования передается при каждом вызове функции pthread_create посредством ее последнего аргумента.

В программе 4.2 представлена каждая из потоковых функций.

Поделиться с друзьями: