Мы уже знаем, как программа порождает дочерний процесс. Первоначально он находится в родительской программе, получая копии ее виртуальной памяти, дескрипторов файлов и т.п. Модификация содержимого памяти, закрытие файлов и другие подобные действия в дочернем процессе не влияют на работу родительского процесса и наоборот. С другой стороны, когда программа создает поток, ничего не копируется. Оба потока — старый и новый — имеют доступ к общему виртуальному пространству, общим дескрипторам файлов и другим системным ресурсам. Если, к примеру, один поток меняет значение переменной, это изменение отражается на другом потоке. Точно так же, когда один поток закрывает файл, второй поток теряет возможность работать с этим файлом. В связи с тем что процесс и все его потоки могут выполнять лишь одну программу одновременно, как только одни из потоков вызывает функцию семейства exec()
, все остальные потоки завершаются (естественно, новая программа может создавать собственные потоки).
В Linux реализована библиотека API-функций работы с потоками, соответствующая стандарту POSIX (она называется Pthreads). Все функции и типы данных библиотеки объявлены в файле
. Эти функции не входят в стандартную библиотеку языка С, поэтому при компоновке программы нужно указывать опцию -lpthread
в командной строке.
4.1. Создание потока
Каждому потоку в процессе назначается собственный идентификатор. При ссылке на идентификаторы потоков в программах, написанных на языке С или C++, нужно использовать тип данных pthread_t
.
После создания поток начинает выполнять void*
и возвращают значение аналогичного типа. Этот параметр называется
Функция pthread_create()
создает новый поток. Ей передаются следующие параметры.
■ Указатель на переменную типа pthread_t
, в которой сохраняется идентификатор нового потока.
■ Указатель на объект NULL
, поток будет создан со стандартными атрибутами. Подробнее данная тема обсуждается в разделе 4.1.5, "Атрибуты потоков".
■ Указатель на потоковую функцию. Функция имеет следующий тип:
void* (*)(void*)
■ Значение аргумента потока (тип void*
). Данное значение без каких-либо изменений передается потоковой функции.
Функция pthread_create()
немедленно завершается, и родительский поток переходит к выполнению инструкции, следующей после вызова функции. Тем временем новый поток начинает выполнять потоковую функцию. ОС Linux планирует работу обоих потоков асинхронно, поэтому программа не должна рассчитывать на какую-то согласованность между ними.
Программа, представленная в листинге 4.1, создает поток, который непрерывно записывает символы 'x' в стандартный поток ошибок. После вызова функции pthread_create()
основной поток начинает делать то же самое, но вместо символов 'x' печатаются символы 'o'.
#include
#include
/* Запись символов 'x' в поток stderr.
Параметр не используется.
Функция никогда не завершается. */
void* print_xs(void* unseed) {
while (1)
fputc('x', stderr);
return NULL;
}
/* Основная программа. */
int main() {
pthread_t thread_id;
/* Создание потока. Новый поток выполняет
функцию print_xs(). */
pthread_create(&thread_id, NULL, &print_xs, NULL);
/* Непрерывная запись символов 'o' в поток stderr. */
while (1)
fputc('o', stderr);
return 0;
}
Компиляция и компоновка программы осуществляются следующим образом:
% cc -o thread-create thread-create.c -lpthread
Запустите программу, и вы увидите, что символы 'x' и 'o' чередуются самым непредсказуемым образом.