POSIX Threads
POSIX Threads (сокращённо Pthreads) — это стандарт POSIX.1c (Threads extensions, IEEE Std 1003.1c-1995), определяющий интерфейс программирования приложений (API) для работы с потоками (нитями) выполнения в операционных системах, совместимых с POSIX. Стандарт регламентирует типы данных, функции и атрибуты для создания, синхронизации и управления потоками в рамках единого адресного пространства процесса. Pthreads является основным механизмом многопоточности в Unix-подобных системах, включая Linux, FreeBSD, macOS и Solaris, а также реализован в виде библиотеки для Microsoft Windows (например, pthreads-w32).
История
Разработка стандарта POSIX Threads началась в конце 1980-х годов как часть более широких усилий по стандартизации Unix-подобных операционных систем. К тому времени существовало множество несовместимых реализаций потоков (например, в системах SunOS, OSF/1, AIX), что затрудняло переносимость многопоточных приложений. Рабочая группа IEEE 1003.1c, в состав которой входили представители компаний Sun Microsystems, Digital Equipment Corporation, IBM и других, подготовила проект расширения, который был утверждён в 1995 году как часть стандарта POSIX.1-1996 (IEEE Std 1003.1-1996).
В 2001 году стандарт Pthreads был интегрирован в единый стандарт POSIX.1-2001 (IEEE Std 1003.1-2001), объединивший базовые определения POSIX.1-1996, POSIX.1c (Threads) и POSIX.1j (Real-time extensions). Последующие версии (POSIX.1-2008, POSIX.1-2017) лишь уточняли и дополняли API, не меняя его фундаментальной структуры. В настоящее время Pthreads является частью обязательных требований POSIX для сертифицированных систем.
Основные концепции
Поток выполнения
Поток (thread) — это наименьшая единица обработки, которой операционная система может выделять процессорное время. В отличие от процессов, потоки одного процесса разделяют:
- адресное пространство (код, данные, кучу);
- файловые дескрипторы;
- обработчики сигналов;
- идентификатор процесса (PID).
Каждый поток имеет собственный:
- стек вызовов;
- регистры процессора (включая счётчик команд);
- идентификатор потока (thread ID, тип
pthread_t); - маску сигналов и локальные для потока данные (Thread-Local Storage, TLS).
Модель «один к одному»
Большинство современных реализаций Pthreads используют модель «один к одному» (1:1), где каждый пользовательский поток отображается на один поток ядра операционной системы. Это обеспечивает максимальную производительность на многоядерных процессорах, но может приводить к увеличению накладных расходов при создании большого числа потоков. Альтернативные модели («многие к одному», «многие ко многим») исторически применялись в некоторых Unix-системах (например, в ранних версиях Solaris), но в настоящее время практически не используются.
API Pthreads
API Pthreads включает около 100 функций, объединённых в несколько групп. Все функции имеют префикс pthread_, а константы и макросы — префикс PTHREAD_. Заголовочный файл — <pthread.h>. Для линковки требуется библиотека libpthread (в Linux — флаг -lpthread).
Управление потоками
- Создание потока:
pthread_create(pthread_t thread, const pthread_attr_t attr, void (start_routine)(void ), void arg). Функция создаёт новый поток, который начинает выполнение с функцииstart_routine, получающей аргументarg. Атрибуты потока задаются через объектpthread_attr_t(еслиNULL, используются значения по умолчанию). - Завершение потока:
pthread_exit(void *retval). Завершает вызывающий поток и возвращает значениеretval(может быть получено другим потоком черезpthread_join). Если поток не вызываетpthread_exit, он завершается при возврате изstart_routine. - Ожидание потока:
pthread_join(pthread_t thread, void **retval). Блокирует вызывающий поток до завершения указанного потокаthreadи, еслиretvalнеNULL, получает его возвращаемое значение. - Отсоединение потока:
pthread_detach(pthread_t thread). Помечает поток как отсоединённый (detached), после чего его ресурсы автоматически освобождаются при завершении (без необходимости вызыватьpthread_join). - Идентификация:
pthread_self()возвращает идентификатор вызывающего потока;pthread_equal(pthread_t t1, pthread_t t2)сравнивает два идентификатора.
Атрибуты потоков
Объект pthread_attr_t позволяет задавать параметры создаваемого потока:
- Размер стека:
pthread_attr_setstacksize/pthread_attr_getstacksize. - Адрес стека:
pthread_attr_setstack/pthread_attr_getstack(позволяет выделить собственный стек). - Состояние отсоединения:
pthread_attr_setdetachstate(создание потока сразу в отсоединённом состоянии). - Область конкуренции:
pthread_attr_setscope(системная или процессная область; в Linux поддерживается толькоPTHREAD_SCOPE_SYSTEM). - Приоритет планирования:
pthread_attr_setschedparam(требует прав суперпользователя для реального времени).
Синхронизация
Мьютексы (mutexes)
Мьютекс (mutual exclusion) — простейший механизм взаимного исключения, предотвращающий одновременный доступ нескольких потоков к критической секции. Тип — pthread_mutex_t. Основные функции:
pthread_mutex_init/pthread_mutex_destroy— инициализация и уничтожение.pthread_mutex_lock— блокирующая попытка захвата мьютекса.pthread_mutex_trylock— неблокирующая попытка (возвращаетEBUSY, если мьютекс занят).pthread_mutex_unlock— освобождение мьютекса.
Атрибуты мьютекса (pthread_mutexattr_t) позволяют задать тип (normal, recursive, errorcheck) и область действия (процессная или межпроцессная).
Условные переменные (condition variables)
Условные переменные (pthread_cond_t) используются для блокировки потока до наступления определённого условия. Работают всегда в паре с мьютексом. Основные функции:
pthread_cond_wait— атомарно разблокирует мьютекс и блокирует поток до получения сигнала.pthread_cond_signal— пробуждает один поток, ожидающий на условной переменной.pthread_cond_broadcast— пробуждает все ожидающие потоки.
Барьеры (barriers)
Барьер (pthread_barrier_t) — механизм синхронизации, при котором все потоки должны достичь барьера, прежде чем любой из них сможет продолжить выполнение. Полезен для этапов параллельных вычислений.
Блокировки чтения-записи (rwlocks)
Блокировки чтения-записи (pthread_rwlock_t) позволяют множеству потоков одновременно читать данные, но дают исключительный доступ для записи. Реализованы функциями pthread_rwlock_rdlock, pthread_rwlock_wrlock, pthread_rwlock_unlock.
Спин-блокировки (spinlocks)
Спин-блокировки (pthread_spinlock_t) — примитив, при котором поток в ожидании блокировки не засыпает, а циклически проверяет состояние блокировки (активное ожидание). Эффективны только на многоядерных системах при очень коротких критических секциях.
Одноразовая инициализация
Функция pthread_once(pthread_once_t once_control, void (init_routine)(void)) гарантирует, что init_routine будет выполнена ровно один раз, независимо от того, сколько потоков одновременно вызывают pthread_once. Используется для потокобезопасной инициализации глобальных ресурсов.
Локальные данные потока (TLS)
API pthread_key_t позволяет создавать ключи (pthread_key_create), к которым каждый поток может привязать собственное значение (pthread_setspecific) и получить его (pthread_getspecific). При завершении потока может быть вызвана функция-деструктор, связанная с ключом.
Пример использования
```c
include <pthread.h>
include <stdio.h>
include <stdlib.h>
define NUM_THREADS 5
void thread_func(void arg) { int id = (int)arg; printf("Thread %d: Hello from Pthreads!\n", id); pthread_exit(NULL); }
int main() { pthread_t threads[NUM_THREADS]; int ids[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) { ids[i] = i; if (pthread_create(&threads[i], NULL, thread_func, &ids[i]) != 0) { perror("pthread_create"); exit(EXIT_FAILURE); } }
for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); }
return 0; } ```
Компиляция: gcc -pthread example.c -o example
Производительность и ограничения
Накладные расходы
Создание потока в Pthreads относительно дорогостояще (порядка нескольких микросекунд в современных Linux-ядрах), так как требует системного вызова clone и выделения стека. Для высокопроизводительных приложений рекомендуется использовать пулы потоков (thread pools), где потоки создаются заранее и многократно переиспользуются.
Ограничения
- Количество потоков: максимальное число потоков на процесс ограничено системными ресурсами (в Linux — параметром
/proc/sys/kernel/threads-max, обычно десятки тысяч). Создание чрезмерно большого числа потоков может привести к исчерпанию памяти стека. - Отсутствие встроенной поддержки параллельных алгоритмов: Pthreads предоставляет лишь примитивы синхронизации, не предлагая готовых конструкций для распараллеливания циклов или задач (в отличие от OpenMP или Intel TBB).
- Сложность отладки: многопоточные программы подвержены гонкам данных (data races), взаимным блокировкам (deadlocks) и состояниям гонки (race conditions), которые трудно воспроизводимы и диагностируемы.
- Переносимость: хотя стандарт POSIX широко распространён, некоторые расширения (например, барьеры, блокировки чтения-записи) могут отсутствовать в устаревших или минимальных реализациях.
Альтернативы
- OpenMP — более высокоуровневый стандарт для параллельного программирования на C/C++ и Fortran, использующий директивы компилятора (например,
#pragma omp parallel for). Реализации OpenMP часто используют Pthreads в качестве низкоуровневого механизма. - C++11 threads — встроенная в язык C++ библиотека
std::thread, предоставляющая объектно-ориентированный интерфейс, совместимый с Pthreads на платформах POSIX. - Windows Threads — API для работы с потоками в Windows (
CreateThread,WaitForSingleObjectи др.), существенно отличающийся от Pthreads. - GNU Pth — пользовательская библиотека потоков (модель «многие к одному»), не использующая системные вызовы ядра. Обеспечивает переносимость, но не позволяет использовать многоядерные процессоры.
Источники
- IEEE Std 1003.1-2017 — Standard for Information Technology — Portable Operating System Interface (POSIX) Base Specifications, Issue 7.
- Butenhof, D. R. (1997). Programming with POSIX Threads. Addison-Wesley. ISBN 0-201-63392-2.
- Kerrisk, M. (2010). The Linux Programming Interface. No Starch Press. Главы 29–33.
- Stevens, W. R., & Rago, S. A. (2013). Advanced Programming in the UNIX Environment (3rd ed.). Addison-Wesley. Глава 11.
- Документация Linux man-pages:
pthreads(7),pthread_create(3),pthread_mutex_lock(3).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →