Pthreads
Pthreads (сокращение от англ. POSIX Threads) — это стандарт POSIX (IEEE 1003.1c), определяющий интерфейс (API) для многопоточного программирования на языках C и C++. Pthreads предоставляет набор функций, типов данных и атрибутов для создания, синхронизации и управления потоками выполнения (нитями) в рамках одного процесса. Стандарт обеспечивает переносимость многопоточного кода между различными операционными системами, поддерживающими POSIX, включая большинство Unix-подобных систем (Linux, FreeBSD, macOS) и, в урезанном виде, Windows (через сторонние библиотеки).
История
Разработка стандарта Pthreads началась в середине 1990-х годов как часть расширения стандарта POSIX. До этого многопоточность в Unix-системах реализовывалась нестандартными, проприетарными API (например, threads от Sun Microsystems или DCE Threads). Основной целью создания Pthreads было унификация интерфейсов для обеспечения переносимости приложений между разными реализациями Unix.
В 1995 году стандарт POSIX.1c (Threads Extensions) был принят как часть IEEE Std 1003.1-1996. Позже он вошёл в единый стандарт POSIX (IEEE Std 1003.1-2001, известный как POSIX.1-2001). С тех пор Pthreads является обязательным компонентом для сертификации системы как POSIX-совместимой. Наиболее полная и стабильная реализация Pthreads — NPTL (Native POSIX Thread Library), разработанная компанией Red Hat и включённая в ядро Linux начиная с версии 2.6. NPTL пришла на смену более старой библиотеке LinuxThreads, которая имела ряд ограничений (например, несоответствие стандарту в части сигналов и идентификаторов потоков).
Основные концепции
Поток выполнения (thread)
Поток — это наименьшая единица обработки, которой операционная система может выделять процессорное время. В рамках одного процесса все потоки разделяют общее адресное пространство (код, данные, кучу), но имеют собственный стек, регистры и локальные переменные. Это позволяет потокам эффективно обмениваться данными без использования механизмов межпроцессного взаимодействия (IPC), таких как каналы или очереди сообщений.
Идентификатор потока (pthread_t)
Каждый поток в системе Pthreads имеет уникальный идентификатор типа pthread_t. Этот тип может быть целым числом, указателем или структурой в зависимости от реализации. Идентификатор используется для обращения к потоку при его создании, ожидании завершения или изменении его атрибутов.
Атрибуты потока (pthread_attr_t)
Перед созданием потока можно задать его атрибуты: размер стека, политику планирования, приоритет, состояние отсоединения (detached state) и другие. Атрибуты хранятся в объекте типа pthread_attr_t, который инициализируется функцией pthread_attr_init() и уничтожается функцией pthread_attr_destroy().
Синхронизация
Для координации доступа потоков к разделяемым ресурсам Pthreads предоставляет несколько механизмов синхронизации:
- Мьютексы (mutex) — объекты типа
pthread_mutex_t, обеспечивающие взаимное исключение. Поток, захвативший мьютекс, получает монопольный доступ к критической секции. Другие потоки блокируются до его освобождения. Поддерживаются обычные, рекурсивные, проверочные и с защитой от ошибок мьютексы. - Условные переменные (condition variable) — объекты типа
pthread_cond_t, используемые для блокировки потока до наступления определённого условия. Поток может ждать на условной переменной, освобождая мьютекс, и быть разбуженным другим потоком, который изменил условие. - Барьеры (barrier) — объекты типа
pthread_barrier_t, позволяющие синхронизировать группу потоков в заданной точке выполнения. Все потоки ждут, пока последний не достигнет барьера, после чего они продолжают выполнение. - Спин-блокировки (spinlock) — объекты типа
pthread_spinlock_t, реализующие активное ожидание (busy-waiting) вместо блокировки потока. Используются в критических секциях с очень коротким временем выполнения. - Чтение-запись блокировки (rwlock) — объекты типа
pthread_rwlock_t, позволяющие множеству потоков одновременно читать данные, но предоставляющие монопольный доступ для записи.
Основные функции API
Управление потоками
| Функция | Назначение |
|---|---|
pthread_create() | Создание нового потока. Принимает идентификатор потока, атрибуты, функцию потока и аргумент. |
pthread_join() | Ожидание завершения указанного потока. Возвращает возвращаемое значение функции потока. |
pthread_detach() | Перевод потока в отсоединённое состояние. После завершения такого потока его ресурсы автоматически освобождаются. |
pthread_exit() | Завершение текущего потока с возвратом указателя. |
pthread_cancel() | Отправка запроса на отмену выполнения потока. |
pthread_self() | Получение идентификатора текущего потока. |
pthread_equal() | Сравнение двух идентификаторов потоков. |
Синхронизация
| Функция | Назначение |
|---|---|
pthread_mutex_init(), pthread_mutex_destroy() | Инициализация и уничтожение мьютекса. |
pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock() | Захват, попытка захвата и освобождение мьютекса. |
pthread_cond_init(), pthread_cond_destroy() | Инициализация и уничтожение условной переменной. |
pthread_cond_wait(), pthread_cond_signal(), pthread_cond_broadcast() | Ожидание, сигнал одному потоку и сигнал всем ожидающим потокам. |
pthread_barrier_init(), pthread_barrier_wait() | Инициализация и ожидание на барьере. |
pthread_rwlock_init(), pthread_rwlock_rdlock(), pthread_rwlock_wrlock(), pthread_rwlock_unlock() | Работа с блокировками чтения-записи. |
Управление атрибутами
| Функция | Назначение |
|---|---|
pthread_attr_init(), pthread_attr_destroy() | Инициализация и уничтожение объекта атрибутов. |
pthread_attr_setdetachstate(), pthread_attr_getdetachstate() | Установка/получение состояния отсоединения. |
pthread_attr_setstacksize(), pthread_attr_getstacksize() | Установка/получение размера стека потока. |
pthread_attr_setschedpolicy(), pthread_attr_getschedpolicy() | Установка/получение политики планирования (SCHED_FIFO, SCHED_RR, SCHED_OTHER). |
Пример использования
Ниже приведён простейший пример создания и ожидания потока на языке C:
```c
include <stdio.h>
include <pthread.h>
void thread_function(void arg) { int num = (int)arg; printf("Поток получил число: %d\n", *num); return NULL; }
int main() { pthread_t thread; int value = 42;
// Создание потока if (pthread_create(&thread, NULL, thread_function, &value) != 0) { perror("pthread_create"); return 1; }
// Ожидание завершения потока if (pthread_join(thread, NULL) != 0) { perror("pthread_join"); return 1; }
printf("Главный поток завершён\n"); return 0; } ```
Компиляция такого кода в Linux выполняется с флагом -lpthread: ``bash gcc -o example example.c -lpthread ``
Преимущества и недостатки
Преимущества
- Переносимость: код, написанный с использованием Pthreads, может быть скомпилирован и выполнен на любой POSIX-совместимой системе.
- Эффективность: потоки внутри процесса разделяют память, что снижает накладные расходы на переключение контекста по сравнению с процессами.
- Гибкость: широкий набор механизмов синхронизации позволяет решать различные задачи многопоточного программирования.
- Низкоуровневость: прямой контроль над созданием, синхронизацией и завершением потоков.
Недостатки
- Сложность: ручное управление потоками и синхронизацией требует высокой квалификации разработчика и чревато ошибками (гонки данных, взаимные блокировки, инверсия приоритетов).
- Отсутствие встроенной поддержки в Windows: хотя существуют сторонние библиотеки (pthreads-w32), нативная реализация Pthreads в Windows отсутствует.
- Ограниченная масштабируемость: при большом количестве потоков (сотни и тысячи) накладные расходы на переключение контекста и синхронизацию могут стать значительными. Для таких сценариев часто используются более лёгкие модели (например, корутины или фьючерсы).
- Небезопасность: отсутствие встроенной защиты от гонок данных — программист должен самостоятельно обеспечивать корректную синхронизацию.
Альтернативы и современные подходы
В современных C++ (начиная с C++11) появился стандартный многопоточный API (std::thread, std::mutex, std::condition_variable), который является более высокоуровневой обёрткой над Pthreads и обеспечивает лучшую переносимость между платформами. В других языках программирования (Java, Python, Go) многопоточность реализуется через собственные библиотеки и модели (например, горутины в Go). Тем не менее, Pthreads остаётся основным низкоуровневым стандартом для многопоточного программирования в Unix-среде и широко используется в системном программировании, серверных приложениях, базах данных и высоконагруженных системах.
Источники
- IEEE Std 1003.1-2017 (POSIX.1-2017). System Interfaces: Threads.
- Б. Керниган, Р. Пайк. «Практика программирования». Глава 6 (Многопоточность).
- W. Richard Stevens, Stephen A. Rago. «Advanced Programming in the UNIX Environment». Глава 11 (Threads).
- Документация библиотеки NPTL (Native POSIX Thread Library) — проект Red Hat.
- Материалы курса «Многопоточное программирование» (Computer Science Center, 2020).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →