• Потоки не имеют иерархии. Любой поток в процессе может воспользоваться функцией 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
Порядок вывода первых двух строчек зависит от того, как планировщик распорядится двумя потоками.
По умолчанию потоки являются
#include
int pthread_detach(pthread_t
Возвращает 0 при успешном завершении или положительное число, если возникла ошибка
В качестве примера использования функции pthread_detach() можно привести следующий вызов, в котором поток отсоединяет сам себя:
pthread_detach(pthread_self());
Если поток уже был отсоединен, мы больше не можем получить его возвращаемый статус с помощью функции pthread_join(). Мы также не можем снова сделать его присоединяемым.
Отсоединенный поток не становится устойчивым к вызову exit(), сделанному в другом потоке, или к инструкции return, выполненной в главной программе. В любой из этих ситуаций все потоки внутри процесса немедленно завершаются, вне зависимости от того, присоединяемые они или нет. Иными словами, функция pthread_detach() просто отвечает за поведение потока после его завершения, но не за то, в каких обстоятельствах тот завершается.