Литмир - Электронная Библиотека
Содержание  
A
A

26.2. Основные функции для работы с потоками: создание и завершение потоков

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

Функция pthread_create

Когда программа запускается с помощью функции

exec
, создается один поток, называемый начальным (initial) или главным (main). Дополнительные потоки создаются функцией
pthread_create
.

#include <pthread.h>

int pthread_create(pthread_t* <i>tid</i>, const pthread_attr_t *<i>attr</i>,

 void *(*<i>func</i>)(void*), void *<i>arg</i>);

<i>Возвращает: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки</i>

Каждый поток процесса обладает собственным идентификатором потока (thread ID), относящимся к типу данных

pthread_t
(как правило, это
unsigned int
). При успешном создании нового потока его идентификатор возвращается через указатель
tid
.

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

pthread_attr_t
, что позволяет заменить значение, заданное по умолчанию. Обычно мы используем значение по умолчанию, в этом случае мы задаем аргумент
attr
равным пустому указателю.

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

pthread_exit
), либо неявно (когда вызванная функция возвращает управление). Адрес функции задается аргументом
func
, и она вызывается с единственным аргументом-указателем
arg
. Если этой функции необходимо передать несколько аргументов, следует поместить их в некоторую структуру и передать адрес этой структуры как единственный аргумент функции.

Обратите внимание на объявления

func
и
arg
. Функции передается один аргумент — универсальный указатель
void*
. Это позволяет нам передавать потоку с помощью единственного указателя все, что требуется, и точно так же поток возвращает любые данные, используя этот указатель.

Возвращаемое значение функций Pthreads — это обычно 0 в случае успешного выполнения или ненулевая величина в случае ошибки. Но в отличие от функций сокетов и большинства системных вызовов, для которых в случае ошибки возвращается -1 и переменной

errno
присваивается некоторое положительное значение (код ошибки), функции Pthreads возвращают сам код ошибки. Например, если функция
pthread_create
не может создать новый поток, так как мы превысили допустимый системный предел количества потоков, функция возвратит значение
EAGAIN
. Функции Pthreads не присваивают переменной
errno
никаких значений. Соглашение о том, что 0 является индикатором успешного выполнения, а ненулевое значение — индикатором ошибки, не приводит к противоречию, так как все значения
Exxx
, определенные в заголовочном файле
&lt;sys/errno.h&gt;
, являются положительными. Ни одному из имен ошибок Exxx не сопоставлено нулевое значение.

Функция pthread_join

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

pthread_join
. Сравнивая потоки и процессы Unix, можно сказать, что функция
pthread_create
аналогична функции
fork
, а функция
pthread_join
— функции
waitpid
.

#include &lt;pthread.h&gt;

int pthread_join(pthread_t <i>tid</i>, void **<i>status</i>);

<i>Возвращает: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки</i>

Следует указать идентификатор

tid
того потока, завершения которого мы ждем. К сожалению, нет способа указать, что мы ждем завершения любого потока данного процесса (тогда как при работе с процессами мы могли с помощью функции
waitpid
ждать завершения любого процесса, задав аргумент идентификатора процесса, равный -1). Мы вернемся к этой проблеме при обсуждении листинга 26.11.

Если указатель

status
непустой, то значение, возвращаемое потоком (указатель на некоторый объект), хранится в ячейке памяти, на которую указывает
status
.

Функция pthread_self

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

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

#include &lt;pthread.h&gt;

pthread_t pthread_self(void);

<i>Возвращает: идентификатор вызывающего потока</i>

Сравнивая потоки и процессы Unix, можно отметить, что функция

pthread_self
аналогична функции
getpid
.

Функция pthread_detach

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

pthread_join
. В свою очередь, отсоединенный поток напоминает процесс-демон: когда он завершается, все занимаемые им ресурсы освобождаются и мы не можем отслеживать его завершение. Если один поток должен знать, когда завершится выполнение другого потока, нам следует оставить последний присоединяемым.

Функция

pthread_detach
изменяет состояние потока, превращая его из присоединяемого в отсоединенный.

#include &lt;pthread.h&gt;

int pthread_detach(pthread_t <i>tid</i>);

281
{"b":"225366","o":1}