многопоточное программирование c linux

Русские Блоги

Многопоточный язык программирования c под Linux

Многопоточный язык программирования c под Linux

Применимо к системе Linux
1. Разберитесь в основных понятиях
Процесс: это описание задачи, выполняемой компьютером. Это наименьшая единица, с которой сталкивается операционная система. Операционная система может выполнять множество процессов. Запустить программу, написанную вами, означает позволить операционной системе выполнить процесс своей собственной программы. Операционная система выделит определенное количество ресурсов в соответствии с программой.
Поток: подумайте о программе (процессе), разделите программу на несколько потоков, чтобы добиться одновременной и многозадачной работы.

Компилировать и запускать многопоточные команды в терминале под linux

3. Краткое объяснение приоритета атрибута программного потока.
Справочная статья 1
Справочная статья 2
Атрибуты потока непрозрачны и могут вызывать только связанные функции (аналогично c ++), сначала создайте переменные атрибутов
У потока есть атрибуты, представленные как pthread_attr_t. Он должен быть инициализирован перед обработкой структуры, а после использования его нужно деинициализировать. Ниже приводится исходная функция pthread_attr_t (); параметры в функции, значение по умолчанию может быть установлено позже.

Для приоритета, вообще говоря, мы всегда сначала берем приоритет, модифицируем полученное значение перед его сохранением. Прикрепите код ниже

Примечание: при работе в нескольких потоках могут возникнуть некоторые проблемы.Когда я запускаю его много раз в терминале Linux, несколько раз поток не будет запущен. В частности, я не знаю, почему он не работает O (∩_∩) 〇Haha

Источник

Многопоточное программирование в Linux

Особенности многопоточного программирования в Linux

Младший байт праметра flags содержит номер сигнала, посылаемого родительскому прцессу, когда завершается соданный им потомок. Параметр также может быть установлен с помощью операции побитового или ( | ) со следующими костантами,чтобы указать что именно разделятся между потомком и родителем:

Если CLONE_VM установлен, родитель и потомок выполняются в одном адресном пространтсве. В частности, запись в память выполненная в потомке или родителе, также видна в другом процессе. Общей является и работа с отображаемой памятью, выполняемая с помощью системных вызов mmap(2) и munmap(2).
Если CLONE_VM не установлен, то потомок будет выполняться в своей отдельной копии адресного пространтсва родителя на время вызова __clone. Запись в память, а также отображение файлов в память теперь выполняется процессами независимо, как в случае с fork(2)

Если CLONE_FS установлен, родитель и потомок используют общую информацию о файловой системе. Сюда входит корневой каталог, текущий каталог и параметр umask процесса. Любой вызов chroot(2), chdir(2), or umask(2), выполненный либо потомком, либо родителем влияет также и на другой поцесс.
Если CLONE_FS не установлен, для потомка создается копия информации о файловой системе родителя но момент вызова __clone. Вызовы chroot(2), chdir(2), umask(2) выполненные процессами не влияют на другой процесс.

Если CLONE_FILES установлен, родитель и потомок будут использовать общую таблицу файловых дескриторов. Эти дескрипторы будут всегда ссылаться на одни и те же файлы и в родительском процессе и в потомке. Любой файловый дескриптор открытый в одном процессе может быть использован в другом. То же самое относится и к закрытию дескриптора с помощью close(2) или изменению его флпгов с помощью fcntl(2).
Еслт CLONE_FILES не установлен, потомок получает копию файловых дескритпоров родителя на момент выполнения __clone. Все операции над фйловыми дескрипторами проводятся процессами независимо друг от друга.

Если CLONE_SIGHAND установлен, родитель и потомок используют общую таблицу обработчиков сигналов. Если потомок или родитель вызывают sigaction(2) чтобы изменить реакцию на сигнал, то эти изменения происходят и в другом процессе. Тем не менее, оба процесса имеют раздельные сигнальные маски. Таким образом, каждый из них может блокировать или разблокировать сигнал используя sigprocmask(2) не влияя на другой процесс.
Если CLONE_SIGHAND не установлен, потомок получает копию таблицы обработчиков синалов на момент вызова __clone. Вызовы sigaction(2) выполненные позже в одном из процессов не влияют на другой.

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

Таким образом, для создания потока нужно задать flags как
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND.

Здесь надо учитывать, что стек на большинстве платформ растет вниз, т.е. в качестве параметра child_stack надо передавать верхний адрес выделенного адресного пространства.

Согласно POSIX, системный вызов fork(2) создает новый процесс состоящий только из одного потока, а именно копии того, что вызвал fork(). ( В так называемых UI threads имеется версия fork() создающая полную копию процесса со всеми потоками). Здесь надо не забывать о том, что если в другом потоке какой-либо мутекс был заблокирован, то он останется навсегда заблокирован и в новом процессе. Чтобы избежать этого следует использовать функцию pthread_atfork(2).

Имеет смысл в работающей версии запретить создание core файла с помощью установок setrlimit(2) во избежание возможности получения доступа к какой-нибудь закрытой информации, находившейся в памяти процесса.

Здесь хочется напомнить то, о чем многие забывают работая с Posix threads вообще, а не только с Linuxthreads. Речь идет о thread cancelation, т.е. о прекращении выполнении потока из другого потока с помощью pthread_cancel(). При этом зачастую забывают о стековых обьектах, а при thread cancelation стек не раскручивается. Пример программы, содержащей ошибку приведен ниже. Здесь создается обьект класса A (переменная a) на стеке потока, но в результате вызова функции pthread_cancel() в первоначальном( родительском) потоке th1 завершается, а деструктор обьекта не вызывается.

Выходом является использование динамической памяти с посчетом указателей и установкой cleanup handlers с помощью pthread_cleanup_push/pop.

Кроме этого, мутекс, заблокированный потоком не разблокируется автоматически при принудительном завершении, что может привести к тупиковой ситуации, если какой-либо другой поток попытается заблокировать этот же мутекс. Разблокирование мутекса также следует «поручить» cleanup hadlers.

В библиотеке linuxthreads имеется несколько функций с суффиксом _np ( non portable ).
pthread_cleanup_push_defer_np(3)
pthread_cleanup_pop_restore_np(3)
pthread_kill_other_threads_np(3)
pthread_mutexattr_setkind_np(3)
pthread_mutexattr_getkind_np(3)

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

Стоит избегать использования в программе мутексов типа PTHREAD_MUTEX_RECURSIVE_NP ( рекурсивных мутексов ), их использование замедляет выполнение программы, поэтому лучше хорошо проработать свой код, чтобы избежать повторного блокирования уже заблокированного в этом потоке мутекса.

Источник

Pthreads: Потоки в русле POSIX

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

многопоточное программирование c linux

В этой статье мы познакомимся с POSIX Threads для того, чтобы затем узнать как это все работает в Linux. Не заходя в дебри синхронизации и сигналов, рассмотрим основные элементы Pthreads. Итак, под капотом потоки.

Общие сведения

многопоточное программирование c linux

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

В чем польза множественных потоков исполнения? Возьмем какой-нибудь загруженный веб сервер, например habrahabr.ru. Если бы сервер создавал отдельный процесс для обслуживания каждого http запроса, мы бы ожидали вечно пока загрузится наша страница. Создания нового процесса — дорогостоящее удовольствие для ОС. Даже учитывая оптимизацию за счет копирования при записи, системные вызовы fork и exec создают новые копии страниц памяти и списка файловых описателей. В целом ядро ОС может создать новый поток на порядок быстрее, чем новый процесс.

многопоточное программирование c linux

Таблицы страниц до и после изменения общей страницы памяти во время копирования при записи.

многопоточное программирование c linux

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

Закон Амдаля для распараллеливания процессов.

многопоточное программирование c linux

Используя уравнение, показанное на рисунке, можно вычислить максимальное улучшение производительности системы, использующей N процессоров и фактор F, который указывает, какая часть системы не может быть распараллелена. Например 75% кода запускается параллельно, а 25% — последовательно. В таком случае на двухядерном процессоре будет достигнуто 1.6 кратное ускорение программы, на четырехядерном процессоре — 2.28571 кратное, а предельное значение ускорения при N стремящемся к бесконечности равно 4.

Отображение потоков в режим ядра

Практически все современные ОС — включая Windows, Linux, Mac OS X, и Solaris — поддерживают управление потоками в режиме ядра. Однако потоки могут быть созданы не только в режиме ядра, но и в режиме пользователя. При использовании этого уровня ядро не знает о существовании потоков — все управление потоками реализуется приложением с помощью специальных библиотек. Пользовательские потоки по разному отображаются на потоки в режиме ядра. Всего существует три модели, из которых 1:1 является наиболее часто используемой.

Отображение N:1

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

многопоточное программирование c linux

Отображение 1:1

Это самая проста модель, в которой каждый поток созданный в каком-нибудь процессе непосредственно управляется планировщиком ядра ОС и отображается на один единственный поток в режиме ядра. Чтобы приложение не плодило бесконтрольно потоки, перегружая ОС, вводят ограничение на максимальное количество потоков поддерживаемых в ОС. Данный способ отображения потоков поддерживают ОС Linux и Windows.

многопоточное программирование c linux

Отображение M:N

При таком подходе M пользовательских потоков мультиплексируются в такое же или меньшее N количество потоков ядра. Преодолеваются негативные эффекты двух других моделей: нити по-настоящему исполняются параллельно и нет необходимости в ОС вводить ограничения на их общее количество. Вместе с тем данную модель довольно трудно реализовать с точки зрения программирования.

многопоточное программирование c linux

Потоки POSIX

В конце 1980-х и начале 1990-х было несколько разных API, но в 1995 г. POSIX.1c стандартизовал потоки POSIX, позже это стало частью спецификаций SUSv3. В наше время многоядерные процессоры проникли даже в настольные ПК и смартфоны, так что у большинства машин есть низкоуровневая аппаратная поддержка, позволяющая им одновременно выполнять несколько потоков. В былые времена одновременное исполнение потоков на одноядерных ЦПУ было лишь впечатляюще изобретательной, но очень эффективной иллюзией.

Pthreads определяет набор типов и функций на Си.

Из man errno
Переменная errno определена в стандарте ISO C как изменяемое lvalue int и не объявляемая явно; errno может быть и макросом. Переменная errno является локальным значением нити; её изменение в одной нити не влияет на её значение в другой нити.

Создание потока

При удачном завершении pthread_create() возвращает код 0, ненулевое значение сигнализирует об ошибке.

Рассмотрим теперь пример многопоточной программы.

Завершение потока

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

Синтаксис проще, чем при создании потока.

Ожидание потока

При удачном завершении pthread_join() возвращает код 0, ненулевое значение сигнализирует об ошибке.

Досрочное завершение потока

При удачном завершении pthread_cancel() возвращает код 0, ненулевое значение сигнализирует об ошибке.

Небольшая иллюстрация создания и отмены потока.

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

многопоточное программирование c linux

Отсоединение потока

При удачном завершении pthread_detach() возвращает код 0, ненулевое значение сигнализирует об ошибке.

Потоки versus процессы

Напоследок предлагаю рассмотреть несколько соображений на тему, следует ли проектировать приложение многопоточным или запускать его в несколько процессов с одним потоком? Сперва выгоды параллельных множественных потоков.

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

Теперь немного о недостатках.

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

Источник

Многопоточное программирование c linux

Прежде чем приступать к программированию потоков, следует ответить на вопрос, а нужны ли они вам. Мы уже знаем, насколько хорошо развиты в Linux средства межпроцессного взаимодействия. С помощью управления процессами в Linux можно решить многие задачи, которые в других ОС решаются только с помощью потоков. Потоки часто становятся источниками программных ошибок особого рода. Эти ошибки возникают при использовании потоками разделяемых ресурсов системы (например, общего адресного пространства) и являются частным случаем более широкого класса ошибок – ошибок синхронизации. Если задача разделена между независимыми процессами, то доступом к их общим ресурсам управляет операционная система, и вероятность ошибок из-за конфликтов доступа снижается. Впрочем, разделение задачи между несколькими независимыми процессами само по себе не защитит вас от других разновидностей ошибок синхронизации. В пользу потоков можно указать то, что накладные расходы на создание нового потока в многопоточном приложении ниже, чем накладные расходы на создание нового самостоятельного процесса. Уровень контроля над потоками в многопоточном приложении выше, чем уровень контроля приложения над дочерними процессами. Кроме того, многопоточные программы не склонны оставлять за собой вереницы зомби или «осиротевших» независимых процессов.

Первая подсистема потоков в Linux появилась около 1996 года и называлась, без лишних затей, – LinuxThreads. Рудимент этой подсистемы, который вы найдете в любой современной системе Linux, – файл /usr/include/pthread.h, указывает год релиза – 1996 и имя разработчика – Ксавье Лерой (Xavier Leroy). Библиотека LinuxThreads была попыткой организовать поддержку потоков в Linux в то время, когда ядро системы еще не предоставляло никаких специальных механизмов для работы с потоками. Позднее разработку потоков для Linux вели сразу две конкурирующие группы – NGPT и NPTL. В 2002 году группа NGPT фактически присоединилась к NPTL и теперь реализация потоков NPTL является стандартом Linux. Подсистема потоков Linux стремится соответствовать требованиям стандартов POSIX, так что новые многопоточные приложения Linux должны без проблем компилироваться на новых POSIX-совместимых системах.

Потоки и процессы

Интересно рассмотреть механизм, с помощью которого Linux решает проблему идентификаторов процессов потоков. В Linux у каждого процесса есть идентификатор. Есть он, естественно, и у процессов-потоков. С другой стороны, спецификация POSIX 1003.1c требует, чтобы все потоки многопоточного приложения имели один идентификатор. Вызвано это требование тем, что для многих функций системы многопоточное приложение должно представляться как один процесс с одним идентификатором. Проблема единого идентификатора решается в Linux весьма элегантно. Процессы многопоточного приложения группируются в группы потоков (thread groups). Группе присваивается идентификатор, соответствующий идентификатору первого процесса многопоточного приложения. Именно этот идентификатор группы потоков используется при «общении» с многопоточным приложением. Функция getpid(2), возвращает значение идентификатора группы потока, независимо от того, из какого потока она вызвана. Функции kill() waitpid() и им подобные по умолчанию также используют идентификаторы групп потоков, а не отдельных процессов. Вам вряд ли понадобится узнавать собственный идентификатор процесса-потока, но если вы захотите это сделать, вам придется воспользоваться довольно экзотичной конструкцией. Получить идентификатор потока (thread ID) можно с помощью функции gettid(2), однако саму функцию нужно еще определить с помощью макроса _syscall. Работа с функцией gettid() выглядит примерно так:

Более подробную информацию вы можете получить на страницах man, посвященных gettid() и _syscall. Потоки создаются функцией pthread_create(3), определенной в заголовочном файле

. Первый параметр этой функции представляет собой указатель на переменную типа pthread_t, которая служит идентификатором создаваемого потока. Второй параметр, указатель на переменную типа pthread_attr_t, используется для передачи атрибутов потока. Третьим параметром функции pthread_create() должен быть адрес функции потока. Эта функция играет для потока ту же роль, что функция main() – для главной программы. Четвертый параметр функции pthread_create() имеет тип void *. Этот параметр может использоваться для передачи значения, возвращаемого функцией потока. Вскоре после вызова pthread_create() функция потока будет запущена на выполнение параллельно с другими потоками программы. Таким образом, собственно, и создается новый поток. Я говорю, что новый поток запускается «вскоре» после вызова pthread_create() потому, что перед тем как запустить новую функцию потока, нужно выполнить некоторые подготовительные действия, а поток-родитель между тем продолжает выполняться. Непонимание этого факта может привести вас к ошибкам, которые трудно будет обнаружить. Если в ходе создания потока возникла ошибка, функция pthread_create() возвращает ненулевое значение, соответствующее номеру ошибки.

Функция потока должна иметь заголовок вида:

Функция pthread_exit() представляет собой потоковый аналог функции _exit(). Аргумент функции pthread_exit(), значение типа void *, становится возвращаемым значением функции потока. Как (и кому?) функция потока может вернуть значение, если она не вызывается из программы явным образом? Для того, чтобы получить значение, возвращенное функцией потока, нужно воспользоваться функцией pthread_join(3). У этой функции два параметра. Первый параметр pthread_join(), – это идентификатор потока, второй параметр имеет тип «указатель на нетипизированный указатель». В этом параметре функция pthread_join() возвращает значение, возвращенное функцией потока. Конечно, в многопоточном приложении есть и более простые способы организовать передачу данных между потоками. Основная задача функции pthread_join() заключается, однако, в синхронизации потоков. Вызов функции pthread_join() приостанавливает выполнение вызвавшего ее потока до тех пор, пока поток, чей идентификатор передан функции в качестве аргумента, не завершит свою работу. Если в момент вызова pthread_join() ожидаемый поток уже завершился, функция вернет управление немедленно. Функцию pthread_join() можно рассматривать как эквивалент waitpid(2) для потоков. Эта функция позволяет вызвавшему ее потоку дождаться завершения работы другого потока. Попытка выполнить более одного вызова pthread_join() (из разных потоков) для одного и того же потока приведет к ошибке.

Посмотрим, как все это работает на практике. Ниже приводится фрагмент листинга программы threads, (полный текст программы вы найдете в исходниках программы в файле threads.c).

Рассмотрим сначала функцию thread_func(). Как вы, конечно, догадались, это и есть функция потока. Наша функция потока очень проста. В качестве аргумента ей передается указатель на переменную типа int, в которой содержится номер потока. Функция потока распечатывает этот номер несколько раз с интервалом в одну секунду и завершает свою работу. В функции main() вы видите две переменных типа pthread_t. Мы собираемся создать два потока и у каждого из них должен быть свой идентификатор. Вы также видите две переменные типа int, id1 и id2, которые используются для передачи функциям потоков их номеров. Сами потоки создаются с помощью функции pthread_create().В этом примере мы не модифицируем атрибуты потоков, поэтому во втором параметре в обоих случаях передаем NULL. Вызывая pthread_create() дважды, мы оба раза передаем в качестве третьего параметра адрес функции thread_func, в результате чего два созданных потока будут выполнять одну и ту же функцию. Функция, вызываемая из нескольких потоков одновременно, должна обладать свойством реентерабельности (этим же свойством должны обладать функции, допускающие рекурсию). Реентерабельная функция, это функция, которая может быть вызвана повторно, в то время, когда она уже вызвана (отсюда и происходит ее название). Реентерабельные функции используют локальные переменные (и локально выделенную память) в тех случаях, когда их не-реентерабельные аналоги могут воспользоваться глобальными переменными.

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

Для того, чтобы скомпилировать программу threads.c, необходимо дать команду:

Команда компиляции включает макрос _REENTERANT. Этот макрос указывает, что вместо обычных функций стандартной библиотеки к программе должны быть подключены их реентерабельные аналоги. Реентерабельный вариант библиотеки glibc написан таким образом, что вы, скорее всего, вообще не обнаружите никаких различий в работе с реентерабельными функциями по сравнению с их обычными аналогами. Мы указываем компилятору путь для поиска заголовочных файлов и путь для поиска библиотек /usr/include/nptl и /usr/lib/nptl соответственно. Наконец, мы указываем компоновщику, что программа должна быть связана с библиотекой libpthread, которая содержит все специальные функции, необходимые для работы с потоками.

У вас, возможно, возникает вопрос, зачем мы использовали две разные переменные, id1 и id2, для передачи значений двум потокам? Почему нельзя использовать одну переменную, скажем id, для обоих потоков? Рассмотрим такой фрагмент кода:

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

Досрочное завершение потока

Чаще всего функция pthread_setcancelstate() используется для временного запрета завершения потока. Допустим, мы программируем поток, и знаем, что при определенных условиях программа может потребовать его досрочного завершения. Но в нашем потоке есть участок кода, во время выполнения которого завершать поток крайне нежелательно. Мы можем оградить этот участок кода от досрочного завершения с помощью пары вызовов pthread_setcancelstate():

Первый вызов pthread_setcancelstate() запрещает досрочное завершение потока, второй – разрешает. Если запрос на досрочное завершение потока поступит в тот момент, когда поток игнорирует эти запросы, выполнение запроса будет отложено до тех пор, пока функция pthread_setcancelstate() не будет вызвана с аргументом PTHREAD_CANCEL_ENABLE. Что именно произойдет дальше, зависит от более тонких настроек потока. Рассмотрим пример программы (на диске вы найдете ее в файле canceltest.c)

В самом начале функции потока thread_func() мы запрещаем досрочное завершение потока, затем выводим четыре тестовых сообщения с интервалом в одну секунду, после чего разрешаем досрочное завершение. Далее, с помощью функции pthread_testcancel(), мы создаем точку отмены (cancellation point) потока. Если досрочное завершение потока было затребовано, в этот момент поток должен завершиться. Затем мы выводим еще одно диагностическое сообщение, которое пользователь не должен видеть, если программа сработает правильно.

В главной функции программы мы создаем поток, затем дожидаемся, пока значение глобальной переменной i станет больше нуля (это гарантирует нам, что поток уже запретил досрочное завершение) и вызываем функцию pthread_cancel(). После этого мы переходим к ожиданию завершения потока с помощью pthread_join(). Если вы скомпилируете и запустите программу, то увидите, что поток распечатает четыре тестовых сообщения I’m still running! (после первого сообщения главная функция программы выдаст запрос на завершение потока).

Поскольку поток завершится досрочно, последнего тестового сообщения вы не увидите. Интересна роль функции pthread_testcancel(). Как уже отмечалось, эта функция создает точку отмены потока. Зачем нужны особые точки отмены? Дело в том, что даже если досрочное завершение разрешено, поток, получивший запрос на досрочное завершение, может завершить работу не сразу. Если поток находится в режиме отложенного досрочного завершения (именно этот режим установлен по умолчанию), он выполнит запрос на досрочное завершение, только достигнув одной из точек отмены. В соответствии со стандартом POSIX, точками отмены являются вызовы многих «обычных» функций, например open(), pause() и write(). Про функцию printf() в документации сказано, что она может быть точкой отмены, но в Linux при попытке остановиться на printf() происходит нечто странное – поток завершается, но pthread_join() не возвращает управления. Поэтому мы создаем явную точку отмены с помощью вызова pthread_testcancel().

Впрочем, мы можем выполнить досрочное завершение потока, не дожидаясь точек останова. Для этого необходимо перевести поток в режим немедленного завершения, что делается с помощью вызова pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); В этом случае беспокоиться о точках останова уже не нужно. Вызов pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); снова переводит поток в режим отложенного досрочного завершения.

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *