Если главный поток вызовет pthread_exit() вместо exit() или инструкции return, остальные потоки продолжат выполнение.

29.5. Идентификаторы потоков

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

include

pthread_t pthread_self(void);

Возвращает идентификатор вызывающего потока

Идентификаторы потоков внутри приложения можно использовать следующим образом.

• Задействуются различными функциями из состава Pthreads для определения того, в каком потоке они выполняются. В качестве примера можно привести функции pthread_join(), pthread_detach(), pthread_cancel() и pthread_kill(); все они описаны в этой и последующих главах.

• В некоторых приложениях может иметь смысл маркировать динамические структуры данных идентификатором определенного потока. Так мы можем определить создателя и «владельца» структуры; это также позволяет определить поток, который должен выполнить какие-то последующие действия со структурой данных.

Функция pthread_equal() позволяет проверить на тождественность два идентификатора потоков.

include

int pthread_equal(pthread_t t1, pthread_t t2);

Возвращает ненулевое значение, если t1 и t2 равны, в противном случае возвращает 0

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

if (pthread_equal(tid, pthread_self())

printf("tid matches self\n");

Необходимость в функции pthread_equal() возникает из-за того, что тип данных pthread_t должен восприниматься как непрозрачный. В Linux он имеет тип unsigned long, но в других системах он может быть указателем или структурой.

В библиотеке NPTL pthread_t в самом деле является указателем, который приводится к типу unsigned long.

Стандарт SUSv3 не требует, чтобы тип pthread_t был скалярным. Это может быть структура. Таким образом, код для вывода идентификатора потока, представленный выше, не является переносимым (хотя он работает во многих системах, включая Linux, и может пригодиться во время отладки):

pthread_t thr;

printf("Thread ID = %ld\n", (long) thr); /* Неправильно! */

В Linux идентификаторы потоков являются уникальными для всех процессов. Однако в других системах это может быть не так. В стандарте SUSv3 отдельно отмечается, что переносимые приложения не могут полагаться на эти идентификаторы для определения потоков в других процессах. Там же указывается, что поточные библиотеки могут повторно использовать эти идентификаторы после присоединения завершенного потока с помощью функции pthread_join() или после завершения отсоединенного потока (функция pthread_join() будет рассмотрена в следующем разделе, а отсоединенные потоки — в разделе 29.7).

Идентификаторы POSIX- и обычных потоков, возвращаемые системным вызовом gettid() (доступным только в Linux), — это не одно и то же. Идентификатор POSIX-потока присваивается и обслуживается реализацией поточной библиотеки. Идентификатор обычного потока возвращается вызовом gettid() и представляет собой число (похожее на идентификатор процесса), которое назначается ядром. И хотя в реализации библиотеки NPTL используются уникальные идентификаторы потоков, выданные ядром, приложениям зачастую не нужно о них знать (к тому же работа с ними лишает приложения возможности переносимости между разными системами).

29.6. Присоединение к завершенному потоку

Функция pthread_join() ждет завершения потока, обозначенного аргументом thread (если поток уже завершился, она сразу же возвращается). Эта операция называется присоединением.

include

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

Возвращает 0 при успешном завершении или положительное число, если случилась ошибка

Если аргумент retval является ненулевым указателем, функция получает копию возвращаемого значения завершенного потока, то есть значение, указанное при выполнении потоком инструкции return или вызове pthread_exit().

Вызов функции pthread_join() для идентификатора уже присоединенного потока может привести к непредсказуемым последствиям; например, мы можем присоединиться к потоку, который был создан позже и повторно использует тот же идентификатор.

Если поток не отсоединен (см. раздел 29.7), мы должны присоединиться к нему с помощью функции pthread_join(). Если нам не удастся это сделать, завершенный поток превратится в аналог процесса-«зомби» (см. раздел 26.2). Помимо пустой траты ресурсов, это может привести к тому, что мы больше не сможем создавать новые потоки (в случае если накопится достаточное количество потоков-«зомби»).

Процедура, которую выполняет функция pthread_join() для потоков, похожа на действие вызова waitpid() в контексте процессов. Но между ними есть и заметные различия.

Перейти на страницу:

Похожие книги