Открыть сервис

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) — это наименьшая единица обработки, которой операционная система может выделять процессорное время. В отличие от процессов, потоки одного процесса разделяют:

Каждый поток имеет собственный:

  • стек вызовов;
  • регистры процессора (включая счётчик команд);
  • идентификатор потока (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 →