• Потоки не имеют иерархии. Любой поток в процессе может воспользоваться функцией pthread_join() для присоединения к другому потоку в том же процессе. Представьте, к примеру, что поток А создал поток Б, который позже создал поток В; в этом случае поток А может присоединиться к потоку В и наоборот. Это отличается от иерархических отношений между процессами. Если родительский процесс создал потомка с помощью fork(), он и только он может ожидать этого потомка, используя вызов wait(). Между потоком, который вызывает функцию pthread_create(), и потоком, который при этом создается, нет таких отношений.

• Невозможно присоединиться к «любому потоку» (в случае с процессами мы можем это сделать с помощью вызова waitpid(–1, &status, options)); также не существует неблокирующей операции присоединения (аналога вызова waitpid() с флагом WNOHANG). Похожих результатов можно добиться с помощью условных переменных; пример этого будет показан в подразделе 30.2.4.

Ограничение, связанное с тем, что функция pthread_join() может присоединять потоки только при наличии конкретного идентификатора, было создано умышленно. Идея заключается в том, что программа должна присоединяться только к тем потокам, о которых она «знает». Проблема операции «присоединения к произвольному потоку» произрастает из того факта, что потоки не имеют иерархии, поэтому таким образом мы действительно могли бы присоединиться к любому потоку, в том числе и к приватному, созданному библиотечной функцией (применение условных переменных, описанное в подразделе 30.2.4, позволяет присоединяться только к известным потокам). В результате библиотека не смогла бы больше присоединиться к этому потоку, чтобы получить его статус, а попытки присоединения к уже присоединенному потоку привели бы к ошибкам. Иными словами, операция «присоединения к произвольному потоку» несовместима с модульной архитектурой приложения.

Пример программы

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

Листинг 29.1. Простая программа, использующая библиотеку Pthreads

threads/simple_thread.c

#include

#include "tlpi_hdr.h"

static void *

threadFunc(void *arg)

{

char *s = (char *) arg;

printf("%s", s);

return (void *) strlen(s);

}

int

main(int argc, char *argv[])

{

pthread_t t1;

void *res;

int s;

s = pthread_create(&t1, NULL, threadFunc, "Hello world\n");

if (s!= 0)

errExitEN(s, "pthread_create");

printf("Message from main()\n");

s = pthread_join(t1, &res);

if (s!= 0)

errExitEN(s, "pthread_join");

printf("Thread returned %ld\n", (long) res);

exit(EXIT_SUCCESS);

}

threads/simple_thread.c

Запустив эту программу, мы увидим следующее:

$ ./simple_thread

Message from main()

Hello world

Thread returned 12

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

29.7. Отсоединение потока

По умолчанию потоки являются присоединяемыми; это означает, что после завершения их статус можно получить из другого потока с помощью функции pthread_join(). Иногда статус, возвращаемый потоком, не имеет значения; нам просто нужно, чтобы система автоматически освободила ресурсы и удалила поток, когда тот завершится. В этом случае мы можем пометить поток как отсоединенный, воспользовавшись функцией pthread_detach() и указав идентификатор потока в аргументе thread.

#include

int pthread_detach(pthread_t thread);

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

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

pthread_detach(pthread_self());

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

Отсоединенный поток не становится устойчивым к вызову exit(), сделанному в другом потоке, или к инструкции return, выполненной в главной программе. В любой из этих ситуаций все потоки внутри процесса немедленно завершаются, вне зависимости от того, присоединяемые они или нет. Иными словами, функция pthread_detach() просто отвечает за поведение потока после его завершения, но не за то, в каких обстоятельствах тот завершается.

29.8. Атрибуты потоков
Перейти на страницу:

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