Все потоки одного процесса совместно используют его глобальные переменные, поэтому им легко обмениваться информацией, но это приводит к необходимости синхронизации.
Однако общими становятся не только глобальные переменные. Все потоки одного процесса разделяют:
■ инструкции процесса;
■ большую часть данных;
■ открытые файлы (например, дескрипторы);
■ обработчики сигналов и вообще настройки для работы с сигналами (действие сигнала);
■ текущий рабочий каталог;
■ идентификаторы пользователя и группы пользователей.
У каждого потока имеются собственные:
■ идентификатор потока;
■ набор регистров, включая счетчик команд и указатель стека;
■ стек (для локальных переменных и адресов возврата);
■ переменная errno
;
■ маска сигналов;
■ приоритет.
ПРИМЕЧАНИЕ
Как сказано в разделе 11.18, можно рассматривать обработчик сигнала как некую разновидность потока. В традиционной модели Unix у нас имеется основной поток выполнения и обработчик сигнала (другой поток). Если в основном потоке в момент возникновения сигнала происходит корректировка связного списка и обработчик сигнала также пытается изменить связный список, обычно начинается путаница. Основной поток и обработчик сигнала совместно используют одни и те же глобальные переменные, но у каждого из них имеется свой собственный стек.
В этой книге мы рассматриваем потоки POSIX, которые также называются Pthreads (POSIX threads). Они были стандартизованы в 1995 году как часть POSIX.1c и будут поддерживаться большинством версий Unix. Мы увидим, что все названия функций Pthreads начинаются с символов pthread_
. Эта глава является введением в концепцию потоков, необходимым для того, чтобы в дальнейшем мы могли использовать потоки в наших сетевых приложениях. Более подробную информацию вы можете найти в [15].
26.2. Основные функции для работы с потоками: создание и завершение потоков
В этом разделе мы рассматриваем пять основных функций для работы с потоками, а в следующих двух разделах мы используем эти функции для написания потоковой модификации клиента и сервера TCP.
Когда программа запускается с помощью функции exec
, создается один поток, называемый начальным ( initial ) или главным ( main ). Дополнительные потоки создаются функцией pthread_create
.
#include
int pthread_create(pthread_t* tid , const pthread_attr_t * attr ,
void *(* func )(void*), void * arg );
Возвращает: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки
Каждый поток процесса обладает собственным идентификатором потока ( 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
, определенные в заголовочном файле , являются положительными. Ни одному из имен ошибок Exxx не сопоставлено нулевое значение.
Читать дальше
Конец ознакомительного отрывка
Купить книгу