Научная статья на тему 'МНОГОПОТОЧНАЯ ОБРАБОТКА В POSIX СТАНДАРТЕ'

МНОГОПОТОЧНАЯ ОБРАБОТКА В POSIX СТАНДАРТЕ Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
41
6
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
МНОГОПОТОЧНАЯ ОБРАБОТКА / POSIX THREADS СТАНДАРТ / РАСПАРАЛЛЕЛИВАНИЕ НА УРОВНЕ ПОТОКОВ / РАСПАРАЛЛЕЛИВАНИЕ НА УРОВНЕ ПРОЦЕССОВ / МЬЮТЕКС

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Волосатова Тамара Михайловна, Киселев Игорь Алексеевич, Князева Светлана Вадимовна

В статье рассматривается реализация многопоточного подхода для вычислений и обработки данных. Описываются отличия распараллеливания на уровне потоков от распараллеливания на уровне процессов. Рассмотрены основные положения стандарта Pthreads для реализации многопоточной обработки.

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Волосатова Тамара Михайловна, Киселев Игорь Алексеевич, Князева Светлана Вадимовна

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

MULTITHREADING IN POSIX STANDARD

The article discusses the implementation of multithreading for computing and data processing. The differences between thread level parallelization and process level parallelization are described. Basic terms of Pthread standard for multithreading process implementation are considered.

Текст научной работы на тему «МНОГОПОТОЧНАЯ ОБРАБОТКА В POSIX СТАНДАРТЕ»

ГРНТИ 20.01.07 УДК 004

Volosatova T.M.

candidate of technical sciences, associate professor, Bauman Moscow State Technical University

Kiselev I.A.

candidate of technical sciences, associate professor, Bauman Moscow State Technical University

Knyazeva S.V. 2nd year master's student, Bauman Moscow State Technical University

MULTITHREADING IN POSIX STANDARD

Волосатова Тамара Михайловна

кандидат технических наук, доцент, Московский государственный технический университет им. Н. Э. Баумана

Киселев Игорь Алексеевич кандидат технических наук, доцент, Московский государственный технический университет им. Н. Э. Баумана

Князева Светлана Вадимовна магистрант 2-го курса,

Московский государственный технический университет им. Н. Э. Баумана

МНОГОПОТОЧНАЯ ОБРАБОТКА В POSIX СТАНДАРТЕ

DOI: 10.31618/ESSA.2782-1994.2021.3.76.214

Summary. The article discusses the implementation of multithreading for computing and data processing. The differences between thread level parallelization and process level parallelization are described. Basic terms of Pthread standard for multithreading process implementation are considered.

Аннотация. В статье рассматривается реализация многопоточного подхода для вычислений и обработки данных. Описываются отличия распараллеливания на уровне потоков от распараллеливания на уровне процессов. Рассмотрены основные положения стандарта Pthreads для реализации многопоточной обработки.

Key words: multithreading, POSIX Threads standard, thread level parallelization, process level parallelization, mutex

Ключевые слова: многопоточная обработка, POSIX Threads стандарт, распараллеливание на уровне потоков, распараллеливание на уровне процессов, мьютекс

Введение. Большинство современных ОС (операционные системы) обладают свойством многозадачности, под которым понимается обеспечение возможности параллельной (или псевдопараллельной) обработки нескольких процессов. Каждая из задач может выполняться, используя несколько потоков обработки, что значительно повышает производительность вычислений. Многопоточная обработка позволяет разработчику проще масштабировать

пользовательские приложения и сервера. В настоящее время данный подход нашел широкое применение, однако при его реализации значительно усложняется разработка

программного кода и его последующая отладка.

Под потоками подразумевают множественные нити исполнения в одном процессе. По сути это базовая единица загрузки ЦПУ (центральное процессорное устройство), состоящая из идентификатора потока, счетчика, регистров и стека. Потоки внутри одного процесса делят секции кода, данных, а также различные ресурсы.

Основная часть. Исторически использование подхода к распараллеливанию на основе потоков

возник, как средство непосредственного распараллеливания вычислений, при решении которого возникало множество проблем, таких как манипулирование потоками. Это приводило к ряду проблем: взаимоблокировкам (deadlocks), активным блокировкам (livelocks), очередям на блокировках (lock convoys), «топтанию потоков на месте» (two-step dances), конкуренции за блокировки (race conditions), превышению лимита (oversubscription) и множестве других нежелательных влияний при реализации параллельных вычислений.

Распараллеливание на уровне потоков отличается от распараллеливания на уровне процессов. Создание потоков в отличие от процессов требует от ОС меньших ресурсов. ОС изолирует потоки в гораздо меньшей степени, нежели процессы [1]. Все потоки одного процесса используют общие файлы, таймеры, устройства, одну и ту же область оперативной памяти, одно и то же адресное пространство, а так же следующие атрибуты:

- идентификаторы процесса и его родителя;

- идентификаторы сессии;

- учетные данные процесса (идентификаторы пользователя и группы);

- дескрипторы открытых файлов;

- блокировки записей;

- действия сигналов;

- информация, относящаяся к файловой системе (как пример: текущий и корневой каталоги);

- интервальные и POSIX-таймеры;

- значения семафоров и мьютексов;

- ограничения на ресурсы;

- потребленное процессорное время;

- использованные ресурсы и ряд других.

Важно отметить, что данные атрибуты

являются глобальными, т.е. они разделяют одни и те же глобальные переменные. Поскольку каждый поток может иметь доступ к любому виртуальному адресу процесса, один поток может использовать стек другого потока.

Между потоками одного процесса нет того, что называют полной защитой, это объясняется тем, что в этом нет необходимости, поскольку для организации взаимодействия и обмена данными потокам не требуется обращаться к ОС, им достаточно использовать общую память: один поток записывает данные, другой считывает их, с другой стороны, потоки разных процессов по-прежнему хорошо защищены друг от друга (обеспечение thread safety).

Распараллеливание на уровне потоков считается более эффективным подходом. Каждый поток имеет собственный счетчик команд и стек. Задача, решаемая в виде нескольких потоков в рамках одного процесса, может быть выполнена быстрее за счет того, что называют псевдопараллельным выполнением отдельных ее частей.

Особенно эффективно можно использовать многопоточность для выполнения распределенных приложений, например, многопоточный сервер может параллельно выполнять запросы сразу от нескольких клиентов. Наибольший эффект от введения многопоточности при обработке запросов достигается в системах, которые принято называть мультипроцессорными системами, в которых потоки, в том числе и принадлежащие одному процессу, могут выполняться на разных процессорах действительно параллельно, а не псевдопараллельно.

Раньше одновременное исполнение потоков на одноядерных ЦПУ было невозможно. Сейчас вся ВТ (вычислительная техника) строится на основе использования многоядерных процессоров, начиная от смартфонов до персональных компьютеров (ПК). Таким образом, в настоящее время у большинства ВТ есть низкоуровневая аппаратная поддержка, позволяющая им одновременно выполнять несколько потоков.

В конце 1980-х и начале 1990-х годах было несколько разных программных интерфейсов (API) для работы с потоками, а в 1995 году в стандарте

POSIX.1 был описан API-интерфейс POSIX-потоков, который позже вошел в состав стандарта SUSv3, используемый по настоящее время.

Pthreads это стандарт POSIX-реализации потоков, который существует для ОС, таких как Unix и Windows [2]. Заголовочный файл, который необходимо подключить для его использования -pthread.h.

Сразу после первого обращения к программе, т.е. его запуска, итоговый процесс состоит из одного потока, который называют исходным или главным. Далее необходимо создать новый поток, для чего используется функция: int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start)(void *), void *arg), где:

- thread - адрес для хранения идентификатора создаваемого потока типа pthread_t;

- start - указатель на потоковую void * функцию (сама функция принимает бестиповый указатель в качестве единственной переменной);

- arg - бестиповый указатель, содержащий аргументы потока (arg указывает на глобальную или динамическую переменную, если вызываемая функция не требует наличия аргументов, то в качестве arg - указать NULL);

- attr - бестиповый указатель атрибутов потока pthread_attr_t. (если этот аргумент = NULL, то поток создается с атрибутами по умолчанию) [3].

Поток завершает выполнение задачи когда:

- потоковая функция выполняет return и возвращает результат произведенных вычислений;

- в результате вызова завершения исполнения потока pthread_exit();

- в результате вызова отмены потока pthread_cancel();

- одна из нитей совершает вызов exit();

- основная нить в функции main() выполняет return, и в таком случае все нити процесса резко сворачиваются.

Функция void pthread_exit(void *value_ptr) -завершает исполнение потока/нитей и освобождает всю память, занятую данными нити.

В отличие от функции exit, функция pthread_exit не очищает ресурсы системы (т.е. например открытый файл, куда идет запись -останется). Следует учитывать, что если нить выходит с помощью pthread_exit() остальные нити будут исполняться. Если выход происходит через exit() или return, то нити исполняться не будут. Поэтому, целесообразно всегда завершать работу с помощью pthread_exit(), иначе может произойти неявный вызов exit().

Функция int pthread_join (pthread_t THREAD_ID, void ** DATA) предназначена для ожидания завершения потока обозначенного THREAD_ID. Фактически эта функция нужна для синхронизации потоков.

Если этот поток к тому времени был уже завершен, то функция немедленно возвращает значение.

Функция int pthread_cancel(pthread_t thread) отменяет выполнение заданного потока, где:

- pthread_t - идентификатор потока, если не получается найти поток по его идентификатору, то функция завершается ошибкой.

Функция int

pthread_mutex_lock(pthread_mutex_t *mutex)

блокирует объекта мьютекса, на который ссылается мьютекс, где:

- pthread_mutex_t *mutex - ссылка на объект мьютекса.

Если мьютекс уже заблокирован, вызывающий поток блокируется до тех пор, пока мьютекс не станет доступен.

В зависимости от поставленной задачи возможно использование следующих типов мьютексов:

- PTHREAD_MUTEX_NORMAL - нельзя отследить взаимные блокировки, т.е. при попытке повторно заблокировать мьютекс возникает тупиковая ситуация. Если поток пытается разблокировать мьютекс, который он не заблокировал, или мьютекс, который разблокирован, возникает неопределенная ситуация.

- PTHREAD_MUTEX_ERRORCHECK -присутствует проверка ошибок. Если поток пытается повторно заблокировать мьютекс, который он уже заблокировал, возвращается код ошибки. Если поток пытается разблокировать мьютекс, который он не заблокировал, или мьютекс, который разблокирован, так же происходит возврат кода ошибки.

- PTHREAD_MUTEX_RECURSIVE -присутствует счетчик блокировок. Когда потоку первый раз передается мьютекс, то счетчик блокировок равен единице. Каждый раз, когда поток повторно блокирует этот мьютекс, счетчик блокировок будет увеличиваться на единицу. Каждый раз, когда поток разблокирует мьютекс, счетчик блокировок будет уменьшаться на единицу. Когда счетчик блокировок достигает нуля, мьютекс доступен для блокировки другим потоками. Если поток пытается разблокировать

мьютекс, который он не блокировал, или мьютекс, который разблокирован, возвращается код ошибки.

- PTHREAD_MUTEX_DEFAULT - попытка рекурсивной блокировки мьютекса приводит к неопределенному поведению. Попытка разблокировать мьютекс, если он не был заблокирован вызывающим потоком, приводит к неопределенному поведению. Попытка разблокировать мьютекс, если он ничем не заблокирован так же приводит к неопределенному поведению.

Функция int

pthread_mutex_trylock(pthread_mutex_t *mutex) дает возможность попытаться заблокировать поток, а функция int

pthread_mutex_unlock(pthread_mutex_t *mutex) разблокирует поток и освобождает мьютекс в любом случае.

Заключение. Технология использования многопоточного подхода при распараллеливании программ имеет большое значение в современном мире и знания о структуре многопоточных приложений необходимо для её реализации.

Ключевой особенностью потоков является более простой обмен информацией по сравнению с процессами. Поэтому некоторые программные архитектуры лучше ложатся на многопоточный подход, чем на многопроцессный. В этих ситуациях потоки могут демонстрировать лучшую производительность (например, поток создается быстрее, чем процесс), однако этот фактор обычно является вторичным при выборе между потоками и процессами.

Список литературы:

1. Таненбаум Э., Архитектура компьютера. 5-е издание - СПб. «Питер», 2011, 843с. - ISBN 9785-469-012740.

2. Abraham Silberschatz, Peter B. Galvin Greg Gagne, Operating System Concepts. 9-th ed. - Wiley, 2012, 992 pp. - ISBN 978-1-118-559635.

3. Michael Kerrisk, Linux Programming Interface. - No Starch Press, 2010, 1552 pp. - ISBN 978-1-593-272203.

i Надоели баннеры? Вы всегда можете отключить рекламу.