Потоки (threads) и многопоточное выполнение программ (multi-threading)
Потоки POSIX (Pthreads)
В качестве конкретной модели многопоточности рассмотрим потоки POSIX (напомним, что данная аббревиатура расшифровывается как Portable Operating Systems Interface of uniX kind – стандарты для переносимых ОС типа UNIX). Многопоточность в POSIX специфицирована стандартом IEEE 1003.1c, который описывает API для создания и синхронизации потоков. Отметим, что POSIX-стандарт API определяет лишь требуемое поведение библиотеки потоков. Реализация потоков оставляется на усмотрение авторов конкретной POSIX-совместимой библиотеки. POSIX-потоки распространены в ОС типа UNIX, а также поддержаны, с целью совместимости программ, во многих других ОС, например, Solaris и Windows NT.
Стандарт POSIX определяет два основных типа данных для потоков: pthread_t – дескриптор потока ; pthread_attr_t – набор атрибутов потока.
Стандарт POSIX специфицирует следующий набор функций для управления потоками:
- pthread_create(): создание потока
- pthread_exit():завершение потока (должна вызываться функцией потока при завершении)
- pthread_cancel():отмена потока
- pthread_join():заблокировать выполнение потока до прекращения другого потока, указанного в вызове функции
- pthread_detach():освободить ресурсы занимаемые потоком (если поток выполняется, то освобождение ресурсов произойдёт после его завершения)
- pthread_attr_init():инициализировать структуру атрибутов потока
- pthread_attr_setdetachstate():указать системе, что после завершения потока она может автоматически освободить ресурсы, занимаемые потоком
- pthread_attr_destroy():освободить память от структуры атрибутов потока (уничтожить дескриптор).
Имеются следующие примитивы синхронизации POSIX-потоков с помощью мьютексов (mutexes) – аналогов семафоров – и условных переменных (conditional variables) – оба эти типа объектов для синхронизации подробно рассмотрены позже в данном курсе:
- - pthread_mutex_init() – создание мьютекса;
- - pthread_mutex_destroy() – уничтожение мьютекса;
- - pthread_mutex_lock() – закрытие мьютекса;
- - pthread_mutex_trylock() – пробное закрытие мьютекса (если он уже закрыт, вызов игнорируется, и поток не блокируется);
- - pthread_mutex_unlock() – открытие мьютекса;
- - pthread_cond_init() – создание условной переменной;
- - pthread_cond_signal() – разблокировка условной переменной;
- - pthread_cond_wait() – ожидание по условной переменной.
Рассмотрим пример использования POSIX-потоков на языке Си.
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <pthread.h> static void wait_thread(void) { time_t start_time = time(NULL); while (time(NULL) == start_time) { // никаких действий, кроме занятия процессора на время до 1 с. } } static void *thread_func(void *vptr_args) { int i; for (i = 0; i < 20; i++) { fputs(" b\n", stderr); wait_thread(); } return NULL; } int main(void) { int i; pthread_t thread; if (pthread_create(&thread, NULL, thread_func, NULL) != 0) { return EXIT_FAILURE; } for (i = 0; i < 20; i++) { puts("a"); wait_thread(); } if (pthread_join(thread, NULL) != 0) { return EXIT_FAILURE; } return EXIT_SUCCESS; }
Пример иллюстрирует параллельное выполнение основного потока, выдающего в стандартный вывод последовательность букв "a", и дочернего потока, выдающего в стандартный поток ошибок (stderr) последовательность букв "b". Обратите внимание на особенности создания потока (pthread_create), указания его тела (исполняемой процедуры потока thread_func) и ожидания завершения дочернего потока (pthread_join).
Потоки и процессы в Solaris
В ОС Solaris, как уже было отмечено, используется модель потоков много / много. Кроме того, в системе используется также уже известное нам понятие облегченный процесс (lightweight process) промежуточное между концепцией пользовательского потока и системного потока. Таким образом, в ОС Solaris каждый пользовательский поток отображается в свой облегченный процесс, который, в свою очередь, отображается в поток ядра; последний может исполняться на любом процессоре (или ядре процессора) компьютерной системы. Схема организации потоков в Solaris изображена на рис. 10.5.
На рис. 10.6 изображена схема организации процесса в ОС Solaris.
На схеме видно, что каждый процесс содержит, кроме стандартной информации блока управления процессом, также список всех своих облегченных процессов для управления ими.