<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>LXF86:Unix API - История изменений</title>
		<link>http://wiki2.linuxformat.ru/index.php?title=LXF86:Unix_API&amp;action=history</link>
		<description>История изменений этой страницы в вики</description>
		<language>ru</language>
		<generator>MediaWiki 1.11.1</generator>
		<lastBuildDate>Thu, 14 May 2026 02:34:19 GMT</lastBuildDate>
		<item>
			<title>Yaleks: викификация</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF86:Unix_API&amp;diff=5977&amp;oldid=prev</link>
			<description>&lt;p&gt;викификация&lt;/p&gt;
&lt;a href=&quot;http://wiki2.linuxformat.ru/index.php?title=LXF86:Unix_API&amp;amp;diff=5977&amp;amp;oldid=5976&quot;&gt;(Различия между версиями)&lt;/a&gt;</description>
			<pubDate>Sun, 14 Dec 2008 12:47:48 GMT</pubDate>			<dc:creator>Yaleks</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF86:Unix_API</comments>		</item>
		<item>
			<title>Yaleks: Новая: {{Цикл/Unix API}} == Потоки: создание и уничтожение == ''ЧАСТЬ 7: Сегодня '''Андрей Боровский''' намерен рассказа...</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF86:Unix_API&amp;diff=5976&amp;oldid=prev</link>
			<description>&lt;p&gt;Новая: {{Цикл/Unix API}} == Потоки: создание и уничтожение == ''ЧАСТЬ 7: Сегодня '''Андрей Боровский''' намерен рассказа...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Цикл/Unix API}}&lt;br /&gt;
== Потоки: создание и уничтожение ==&lt;br /&gt;
''ЧАСТЬ 7: Сегодня '''Андрей Боровский''' намерен рассказать о жизненном цикле потоков – от создания до принудительного завершения.''&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;right&amp;quot;&lt;br /&gt;
|“Processes are like human beings...&amp;lt;br /&amp;gt;A small difference is that sex is not really common among&amp;lt;br /&amp;gt;processes as each process has just one parent.”&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div align=&amp;quot;right&amp;quot;&amp;gt;Understanding the Linux Kernel, 3rd Edition&amp;lt;br /&amp;gt;By Daniel P. Bovet, Marco Cesati&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&amp;lt;div style=&amp;quot;clear:both;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Многопоточность является естественным продолжением&lt;br /&gt;
многозадачности, точно так же как виртуальные машины,&lt;br /&gt;
позволяющие запускать несколько ОС на одном компьютере, представляют собой логическое развитие концепции разделения&lt;br /&gt;
ресурсов. В рамках неформального, но простого, определения, поток –&lt;br /&gt;
это последовательность выполнения машинных инструкций. В многопоточном приложении одновременно работает несколько потоков.&lt;br /&gt;
Некоторые авторы избегают термина «поток» и используют вместо&lt;br /&gt;
него термин «нить» (от англ. «thread»), вероятно для того, чтобы потоки программ не путались с потоками ввода-вывода. Для обозначения&lt;br /&gt;
последовательного выполнения цепочки инструкций мне лично больше нравится термин «поток», которым я и буду пользоваться. Надеюсь,&lt;br /&gt;
читатели Linux Format не запутаются в контекстах и, встретив слово&lt;br /&gt;
поток, всегда поймут, идет ли речь о потоках программы, потоках ввода вывода, или о бурных паводковых потоках.&lt;br /&gt;
&lt;br /&gt;
Прежде чем приступать к программированию потоков, следует&lt;br /&gt;
ответить на вопрос: а нужны ли они вам. Мы уже знаем, насколько&lt;br /&gt;
хорошо развиты в Linux средства межпроцессного взаимодействия.&lt;br /&gt;
С помощью управления процессами в Linux можно решить многие&lt;br /&gt;
задачи, которые в других ОС решаются только с помощью потоков.&lt;br /&gt;
Потоки часто становятся источниками программных ошибок особого&lt;br /&gt;
рода. Эти ошибки возникают при использовании потоками разделяемых ресурсов системы (например, общего адресного пространства) и&lt;br /&gt;
являются частным случаем более широкого класса ошибок – ошибок&lt;br /&gt;
синхронизации. Если задача разделена между независимыми процессами, то доступом к их общим ресурсам управляет операционная&lt;br /&gt;
система, и вероятность ошибок из-за конфликтов доступа снижается. Впрочем, разделение задачи между несколькими независимыми&lt;br /&gt;
процессами само по себе не защитит вас от других разновидностей&lt;br /&gt;
ошибок синхронизации. В пользу потоков можно указать то, что&lt;br /&gt;
накладные расходы на создание нового потока в многопоточном приложении обычно ниже, чем накладные расходы на создание нового&lt;br /&gt;
самостоятельного процесса. Уровень контроля над потоками в многопоточном приложении выше, чем уровень контроля приложения&lt;br /&gt;
над дочерними процессами. Кроме того, многопоточные программы&lt;br /&gt;
не склонны оставлять за собой вереницы зомби или «осиротевших»&lt;br /&gt;
независимых процессов.&lt;br /&gt;
&lt;br /&gt;
Первая подсистема потоков в Linux появилась около 1996 года и&lt;br /&gt;
называлась без лишних затей – LinuxThreads. Рудимент этой подсистемы, который вы найдете в любой современной системе Linux, – файл&lt;br /&gt;
/usr/include/pthread.h, указывает год выпуска – 1996 и имя разработчика – Ксавье Лерой (Xavier Leroy). Библиотека LinuxThreads была&lt;br /&gt;
попыткой организовать поддержку потоков в Linux в то время, когда&lt;br /&gt;
ядро системы еще не предоставляло никаких специальных механизмов&lt;br /&gt;
для работы с ними. Позднее разработку потоков для Linux вели сразу&lt;br /&gt;
две конкурирующие группы – NGPT и NPTL. В 2002 году группа NGPT&lt;br /&gt;
фактически присоединилась к NPTL, и теперь реализация потоков&lt;br /&gt;
NPTL является стандартом Linux. Подсистема потоков Linux стремится&lt;br /&gt;
соответствовать требованиям стандартов POSIX, так что новые многопоточные приложения Linux должны без проблем компилироваться на&lt;br /&gt;
других POSIX-совместимых системах.&lt;br /&gt;
&lt;br /&gt;
=== Потоки и процессы ===&lt;br /&gt;
Тем, кто впервые познакомился с концепцией потоков, изучая программирование для Windows, модель потоков Linux покажется непривычной. В среде Microsoft Windows процесс – это контейнер для потоков&lt;br /&gt;
(именно этими словами о процессах говорит Джеффри Рихтер в своей&lt;br /&gt;
классической книге «Программирование приложений для Microsoft&lt;br /&gt;
Windows»). Процесс-контейнер содержит как минимум один поток.&lt;br /&gt;
Если потоков в процессе несколько, приложение (процесс) становится многопоточным. В мире Linux все выглядит иначе. В Linux каждый&lt;br /&gt;
поток является процессом, и для того, чтобы создать новый поток,&lt;br /&gt;
нужно создать новый процесс. В чем же, в таком случае, заключается преимущество многопоточности Linux перед многопроцессностью?&lt;br /&gt;
В многопоточных приложениях Linux для создания дополнительных&lt;br /&gt;
потоков используются процессы особого типа. Эти процессы представляют собой обычные дочерние процессы главного процесса, но&lt;br /&gt;
они разделяют с главным процессом адресное пространство, файловые дескрипторы и обработчики сигналов. Для обозначения процессов этого типа, применяется специальный термин – легкие процессы&lt;br /&gt;
(lightweight processes). Прилагательное «легкий» в названии процессов-потоков вполне оправдано. Поскольку этим процессам не нужно создавать собственную копию адресного пространства (и других ресурсов)&lt;br /&gt;
своего процесса-родителя, создание нового легкого процесса требует&lt;br /&gt;
значительно меньших затрат, чем создание полновесного дочернего&lt;br /&gt;
процесса. Поскольку потоки Linux на самом деле представляют собой&lt;br /&gt;
процессы, в мире Linux нельзя говорить, что один процесс содержит&lt;br /&gt;
несколько потоков. Если вы скажете это, в вас тут же заподозрят вражеского лазутчика!&lt;br /&gt;
&lt;br /&gt;
Интересно рассмотреть механизм, с помощью которого Linux&lt;br /&gt;
решает проблему идентификаторов процессов-потоков. В Linux у каждого процесса есть идентификатор. Есть он, естественно, и у процессов-потоков. С другой стороны, спецификация POSIX 1003.1c требует,&lt;br /&gt;
чтобы все потоки многопоточного приложения имели один идентификатор. Вызвано это требование тем, что для многих функций системы&lt;br /&gt;
многопоточное приложение должно представляться как один процесс&lt;br /&gt;
с одним идентификатором. Проблема единого идентификатора решается в Linux весьма элегантно. Процессы многопоточного приложения&lt;br /&gt;
группируются в группы потоков (thread groups). Группе присваивается&lt;br /&gt;
идентификатор, соответствующий идентификатору первого процесса многопоточного приложения. Именно этот идентификатор группы&lt;br /&gt;
потоков используется при «общении» с многопоточным приложением. Функция {{man|getpid|2}} возвращает значение идентификатора группы&lt;br /&gt;
потока, независимо от того, из какого потока она вызвана. Функции&lt;br /&gt;
kill(), waitpid() и им подобные по умолчанию также используют идентификаторы групп потоков, а не отдельных процессов. Вам вряд ли&lt;br /&gt;
понадобится узнавать собственный идентификатор процесса-потока, но если вы захотите это сделать, вам придется воспользоваться&lt;br /&gt;
довольно экзотичной конструкцией. Получить идентификатор потока&lt;br /&gt;
(thread ID) можно с помощью функции gettid(2), однако саму функцию&lt;br /&gt;
нужно еще определить с помощью макроса _syscall [это само по себе&lt;br /&gt;
уже является явным намеком на то, что вам не следует усердствовать&lt;br /&gt;
с ее использованием, – прим. ред.]. Работа с функцией gettid() выглядит примерно так:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;linux/unistd.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
_syscall0(pid_t,gettid)&lt;br /&gt;
...&lt;br /&gt;
pid_t my_tid;&lt;br /&gt;
my_tid = gettid();&amp;lt;/source&amp;gt;&lt;br /&gt;
Более подробную информацию вы можете получить на страницах&lt;br /&gt;
man, посвященных gettid() и _syscall.&lt;br /&gt;
&lt;br /&gt;
Потоки создаются функцией {{man|pthread_create|3}}, определенной в&lt;br /&gt;
заголовочном файле pthread.h. Первый параметр этой функции представляет собой указатель на переменную типа pthread_t, которая служит идентификатором создаваемого потока. Второй параметр, указатель на переменную типа pthread_attr_t, используется для передачи&lt;br /&gt;
атрибутов потока. Третьим параметром функции pthread_create() должен быть адрес функции потока. Эта функция играет для потока ту же&lt;br /&gt;
роль, что функция main() для главной программы. Четвертый параметр функции pthread_create() имеет тип void *. Этот параметр может&lt;br /&gt;
использоваться для передачи функции потока произвольного аргумента. Вскоре после вызова pthread_create() функция потока будет запущена на выполнение параллельно с другими потоками программы. Таким&lt;br /&gt;
образом, собственно, и создается новый поток. Я говорю, что новый&lt;br /&gt;
поток запускается «вскоре» после вызова pthread_create() потому, что&lt;br /&gt;
перед тем как запустить новую функцию потока, нужно выполнить&lt;br /&gt;
некоторые подготовительные действия, а поток-родитель, между тем,&lt;br /&gt;
продолжает выполняться. Непонимание этого факта может привести&lt;br /&gt;
вас к ошибкам, которые будет трудно обнаружить. Если в ходе создания потока возникла ошибка, функция pthread_create() возвращает&lt;br /&gt;
ненулевое значение, соответствующее номеру ошибки.&lt;br /&gt;
&lt;br /&gt;
Функция потока должна иметь заголовок вида:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;void * func_name(void * arg)&amp;lt;/source&amp;gt;&lt;br /&gt;
Имя функции, естественно, может быть любым. Аргумент arg – это&lt;br /&gt;
тот самый указатель, который передается в последнем параметре&lt;br /&gt;
функции pthread_create(). Функция потока может вернуть значение,&lt;br /&gt;
которое затем будет проанализировано заинтересованным потоком, но&lt;br /&gt;
это не обязательно. Завершение функции потока происходит, если (а)&lt;br /&gt;
функция потока вызвала функцию {{man|pthread_exit|3}}; (б) функция потока достигла точки выхода; (в) поток был досрочно завершен другим&lt;br /&gt;
потоком. Функция pthread_exit() представляет собой потоковый аналог функции _exit(). Аргумент функции pthread_exit(), значение типа&lt;br /&gt;
void *, становится возвращаемым значением функции потока. Как (и&lt;br /&gt;
кому?) функция потока может вернуть значение, если она не вызывается из программы явным образом? Для того, чтобы получить значение, возвращенное функцией потока, нужно воспользоваться функцией {{man|pthread_join|3}}. У этой функции два параметра. Первый параметр,&lt;br /&gt;
pthread_join(), – это идентификатор потока, второй параметр имеет&lt;br /&gt;
тип «указатель на нетипизированный указатель». В этом параметре&lt;br /&gt;
функция pthread_join() возвращает значение, возвращенное функцией&lt;br /&gt;
потока. Конечно, в многопоточном приложении есть и более простые&lt;br /&gt;
способы организовать передачу данных между потоками. Основная&lt;br /&gt;
задача функции pthread_join() заключается, однако, в синхронизации&lt;br /&gt;
потоков. Вызов функции pthread_join() приостанавливает выполнение вызвавшего ее потока до тех пор, пока поток, чей идентификатор передан функции в качестве аргумента, не завершит свою работу.&lt;br /&gt;
Если в момент вызова pthread_join() ожидаемый поток уже завершился, функция вернет управление немедленно. Функцию pthread_join()&lt;br /&gt;
можно рассматривать как эквивалент {{man|waitpid|2}} для потоков. Попытка&lt;br /&gt;
выполнить более одного вызова pthread_join() (из разных потоков) для&lt;br /&gt;
одного и того же потока приведет к ошибке.&lt;br /&gt;
&lt;br /&gt;
Посмотрим, как все это работает на практике. Ниже приводится&lt;br /&gt;
фрагмент листинга программы threads, полный текст которой вы найдете на прилагаемом диске в файле threads.c:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
void * thread_func(void *arg)&lt;br /&gt;
{&lt;br /&gt;
int i;&lt;br /&gt;
int loc_id = * (int *) arg;&lt;br /&gt;
for (i = 0; i &amp;lt; 4; i++) {&lt;br /&gt;
printf(&amp;quot;Thread %i is running\n&amp;quot;, loc_id);&lt;br /&gt;
sleep(1);&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
int main(int argc, char * argv[])&lt;br /&gt;
{&lt;br /&gt;
int id1, id2, result;&lt;br /&gt;
pthread_t thread1, thread2;&lt;br /&gt;
id1 = 1;&lt;br /&gt;
result = pthread_create(&amp;amp;thread1, NULL, thread_func, &amp;amp;id1);&lt;br /&gt;
if (result != 0) {&lt;br /&gt;
perror(&amp;quot;Creating the first thread&amp;quot;);&lt;br /&gt;
return EXIT_FAILURE;&lt;br /&gt;
}&lt;br /&gt;
id2 = 2;&lt;br /&gt;
result = pthread_create(&amp;amp;thread2, NULL, thread_func, &amp;amp;id2);&lt;br /&gt;
if (result != 0) {&lt;br /&gt;
perror(&amp;quot;Creating the first thread&amp;quot;);&lt;br /&gt;
return EXIT_FAILURE;&lt;br /&gt;
}&lt;br /&gt;
result = pthread_join(thread1, NULL);&lt;br /&gt;
if (result != 0) {&lt;br /&gt;
perror(&amp;quot;Joining the first thread&amp;quot;);&lt;br /&gt;
return EXIT_FAILURE;&lt;br /&gt;
}&lt;br /&gt;
result = pthread_join(thread2, NULL);&lt;br /&gt;
if (result != 0) {&lt;br /&gt;
perror(&amp;quot;Joining the first thread&amp;quot;);&lt;br /&gt;
return EXIT_FAILURE;&lt;br /&gt;
}&lt;br /&gt;
printf(&amp;quot;Done\n&amp;quot;);&lt;br /&gt;
return EXIT_SUCCESS;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Рассмотрим сначала функцию thread_func(). Как вы, конечно,&lt;br /&gt;
догадались, это и есть функция потока. Наша функция потока очень&lt;br /&gt;
проста. В качестве аргумента ей передается указатель на переменную типа int, в которой содержится номер потока. Функция потока&lt;br /&gt;
распечатывает этот номер несколько раз с интервалом в одну секунду и завершает свою работу. В функции main() вы видите две переменных типа pthread_t. Мы собираемся создать два потока, и у каждого из них должен быть свой идентификатор. Вы также видите две&lt;br /&gt;
переменные типа int, id1 и id2, которые используются для передачи&lt;br /&gt;
функциям потоков их номеров. Сами потоки создаются с помощью&lt;br /&gt;
функции pthread_create().В этом примере мы не модифицируем атрибуты потоков, поэтому во втором параметре в обоих случаях передаем NULL. Вызывая pthread_create() дважды, мы оба раза передаем в&lt;br /&gt;
качестве третьего параметра адрес функции thread_func, в результате&lt;br /&gt;
чего два созданных потока будут выполнять одну и ту же функцию.&lt;br /&gt;
Функция, вызываемая из нескольких потоков одновременно, должна&lt;br /&gt;
обладать свойством реентерабельности (этим же свойством должны&lt;br /&gt;
обладать функции, допускающие рекурсию). Реентерабельная функция – это функция, которая может быть вызвана повторно, в то время, когда она уже выполняется (отсюда и происходит ее название).&lt;br /&gt;
Реентерабельные функции используют локальные переменные (и&lt;br /&gt;
локально выделенную память) в тех случаях, когда их не-реентерабельные аналоги могут воспользоваться глобальными переменными.&lt;br /&gt;
&lt;br /&gt;
Мы вызываем последовательно две функции pthread_join() для&lt;br /&gt;
того, чтобы дождаться завершения обоих потоков. Если мы хотим&lt;br /&gt;
дождаться завершения всех потоков, порядок вызова функций pthread_join() для разных потоков, очевидно, не имеет значения.&lt;br /&gt;
Для того, чтобы скомпилировать программу threads.c, необходимо&lt;br /&gt;
дать следующую команду:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;gcc threads.c -D_REENTERANT -I/usr/include/nptl -L/usr/lib/nptl --lpthread -o threads&amp;lt;/source&amp;gt;&lt;br /&gt;
Команда компиляции включает макрос _REENTERANT. Этот макрос указывает, что вместо обычных функций стандартной библиотеки&lt;br /&gt;
к программе должны быть подключены их реентерабельные аналоги. Реентерабельный вариант библиотеки glibc написан таким образом, что вы, скорее всего, вообще не обнаружите никаких различий в&lt;br /&gt;
работе с реентерабельными функциями по сравнению с их обычными&lt;br /&gt;
аналогами. Мы указываем компилятору путь для поиска заголовочных&lt;br /&gt;
файлов и путь для поиска библиотек /usr/include/nptl и /usr/lib/nptl&lt;br /&gt;
соответственно. Наконец, мы указываем компоновщику, что программа&lt;br /&gt;
должна быть связана с библиотекой libpthread, которая содержит все&lt;br /&gt;
специальные функции, необходимые для работы с потоками.&lt;br /&gt;
&lt;br /&gt;
У вас, возможно, возникает вопрос, зачем мы использовали две&lt;br /&gt;
разные переменные, id1 и id2, для передачи значений двум потокам?&lt;br /&gt;
Почему нельзя использовать одну переменную, скажем id, для обоих&lt;br /&gt;
потоков? Рассмотрим такой фрагмент кода:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;id = 1;&lt;br /&gt;
pthread_create(&amp;amp;thread1, NULL, thread_func, &amp;amp;id);&lt;br /&gt;
id = 2;&lt;br /&gt;
pthread_create(&amp;amp;thread2, NULL, thread_func, &amp;amp;id);&amp;lt;/source&amp;gt;&lt;br /&gt;
Конечно, в этом случае оба потока получат указатель на одну и ту&lt;br /&gt;
же переменную, но ведь значение этой переменной нужно каждому&lt;br /&gt;
потоку только в самом начале его работы. После того, как поток присвоит это значение своей локальной переменной loc_id, ничто не мешает нам использовать ту же переменную id для другого потока. Все это&lt;br /&gt;
верно, но проблема заключается в том, что мы не знаем, когда первый&lt;br /&gt;
поток начнет свою работу. То, что функция pthread_create() вернула&lt;br /&gt;
управление, не гарантирует нам, что поток уже выполняется. Вполне&lt;br /&gt;
может случиться так, что первый поток будет запущен уже после того,&lt;br /&gt;
как переменной id будет присвоено значение 2. Тогда оба потока получат одно и то же значение id. Впрочем, мы можем использовать одну&lt;br /&gt;
и ту же переменную для передачи данных функциям потока, если воспользуемся средствами синхронизации – им будет посвящена следующая статья.&lt;br /&gt;
&lt;br /&gt;
=== Досрочное завершение потока ===&lt;br /&gt;
Функции потоков можно рассматривать как вспомогательные программы, находящиеся под управлением функции main(). Точно так&lt;br /&gt;
же, как при управлении процессами, иногда у программы возникает необходимость досрочно завершить один из потоков. Для этого&lt;br /&gt;
можно воспользоваться функцией {{man|pthread_cancel|3}}. Единственным&lt;br /&gt;
аргументом этой функции является идентификатор потока. Функция&lt;br /&gt;
pthread_cancel() возвращает 0 в случае успеха и ненулевое значение&lt;br /&gt;
в случае ошибки. Несмотря на то, что pthread_cancel() может завершить поток досрочно, ее нельзя назвать средством принудительного&lt;br /&gt;
завершения потоков. Дело в том, что поток может не только самостоятельно выбрать порядок завершения в ответ на вызов pthread_cancel(), но и вовсе игнорировать этот вызов. Вызов функции pthread_cancel() следует рассматривать как запрос на выполнение досрочного&lt;br /&gt;
завершения потока. Функция {{man|pthread_setcancelstate|3}} определяет,&lt;br /&gt;
будет ли поток реагировать на обращение к нему с помощью pthread_cancel(), или не будет. У функции pthread_setcancelstate() два параметра, параметр state типа int и параметр oldstate типа «указатель на&lt;br /&gt;
int». В первом параметре передается новое значение, указывающее,&lt;br /&gt;
как поток должен реагировать на запрос pthread_cancel(), а в переменную, чей адрес был передан во втором параметре, функция записывает прежнее значение. Если прежнее значение вас не интересует,&lt;br /&gt;
во втором параметре можно передать NULL. Чаще всего функция&lt;br /&gt;
pthread_setcancelstate() используется для временного запрета завершения потока. Допустим, мы программируем поток, и знаем, что при&lt;br /&gt;
определенных условиях программа может потребовать его досрочного завершения. Но в нашем потоке есть участок кода, во время выполнения которого завершать поток крайне нежелательно. Мы можем&lt;br /&gt;
оградить этот участок кода от досрочного завершения с помощью&lt;br /&gt;
пары вызовов pthread_setcancelstate():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);&lt;br /&gt;
... //Здесь поток завершать нельзя&lt;br /&gt;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);&amp;lt;/source&amp;gt;&lt;br /&gt;
Первый вызов pthread_setcancelstate() запрещает досрочное завершение потока, второй – разрешает. Если запрос на досрочное завершение потока поступит в тот момент, когда поток игнорирует эти&lt;br /&gt;
запросы, выполнение запроса будет отложено до тех пор, пока функция pthread_setcancelstate() не будет вызвана с аргументом PTHREAD_CANCEL_ENABLE. Что именно произойдет дальше, зависит от более&lt;br /&gt;
тонких настроек потока. Рассмотрим пример программы (вы найдете&lt;br /&gt;
ее на диске в файле canceltest.c)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
int i = 0;&lt;br /&gt;
void * thread_func(void *arg)&lt;br /&gt;
{&lt;br /&gt;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);&lt;br /&gt;
for (i=0; i &amp;lt; 4; i++) {&lt;br /&gt;
sleep(1);&lt;br /&gt;
printf(&amp;quot;I’m still running!\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);&lt;br /&gt;
pthread_testcancel();&lt;br /&gt;
printf(&amp;quot;YOU WILL NOT STOP ME!!!\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
int main(int argc, char * argv[])&lt;br /&gt;
{&lt;br /&gt;
pthread_t thread;&lt;br /&gt;
pthread_create(&amp;amp;thread, NULL, thread_func, NULL);&lt;br /&gt;
while (i &amp;lt; 1) sleep(1);&lt;br /&gt;
pthread_cancel(thread);&lt;br /&gt;
printf(&amp;quot;Requested to cancel the thread\n&amp;quot;);&lt;br /&gt;
pthread_join(thread, NULL);&lt;br /&gt;
printf(&amp;quot;The thread is stopped.\n&amp;quot;);&lt;br /&gt;
return EXIT_SUCCESS;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
В самом начале функции потока thread_func() мы запрещаем&lt;br /&gt;
досрочное завершение потока, затем выводим четыре тестовых сообщения с интервалом в одну секунду, после чего разрешаем досрочное завершение. Далее, с помощью функции pthread_testcancel(), мы&lt;br /&gt;
создаем точку отмены (cancellation point) нашего потока. Если досрочное завершение потока было затребовано, в этот момент поток должен&lt;br /&gt;
завершиться. Затем мы выводим еще одно диагностическое сообщение, которое пользователь не должен видеть, если программа работает правильно.&lt;br /&gt;
&lt;br /&gt;
В главной функции программы мы создаем поток, затем дожидаемся, пока значение глобальной переменной i станет больше нуля&lt;br /&gt;
(это гарантирует нам, что поток уже запретил досрочное завершение)&lt;br /&gt;
и вызываем функцию pthread_cancel(). После этого мы переходим&lt;br /&gt;
к ожиданию завершения потока с помощью pthread_join(). Если вы&lt;br /&gt;
скомпилируете и запустите программу, то увидите, что поток распечатает четыре тестовых сообщения I’m still running! (после первого&lt;br /&gt;
сообщения главная функция программы выдаст запрос на завершение&lt;br /&gt;
потока). Поскольку поток завершится досрочно, последнего тестового&lt;br /&gt;
сообщения вы не увидите.&lt;br /&gt;
&lt;br /&gt;
Интересна роль функции pthread_testcancel(). Как уже отмечалось,&lt;br /&gt;
эта функция создает точку отмены потока. Зачем нужны особые точки отмены? Дело в том, что даже если досрочное завершение разрешено, поток, получивший запрос на досрочное завершение, может&lt;br /&gt;
остановиться не сразу. Если поток находится в режиме отложенного&lt;br /&gt;
досрочного завершения (именно этот режим установлен по умолчанию), он выполнит запрос на досрочное завершение, только достигнув одной из точек отмены. В соответствии со стандартом POSIX, точками отмены являются вызовы многих «обычных» функций, например open(), pause() и write(). Про функцию printf() в документации&lt;br /&gt;
сказано, что она может быть точкой отмены, но в Linux при попытке&lt;br /&gt;
остановиться на printf() происходит нечто странное – поток завершается, но pthread_join() не возвращает управления. Поэтому мы&lt;br /&gt;
создаем явную точку отмены с помощью вызова pthread_testcancel().&lt;br /&gt;
Впрочем, мы можем выполнить досрочное завершение потока, не&lt;br /&gt;
дожидаясь точек останова. Для этого необходимо перевести поток в&lt;br /&gt;
режим немедленного завершения, что делается с помощью вызова&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);&amp;lt;/source&amp;gt;&lt;br /&gt;
В этом случае беспокоиться о точках останова уже не нужно.&lt;br /&gt;
Вызов&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);&amp;lt;/source&amp;gt;&lt;br /&gt;
снова переводит поток в режим отложенного досрочного&lt;br /&gt;
завершения.&lt;br /&gt;
&lt;br /&gt;
Тема потоков практически неисчерпаема (простите за каламбур),&lt;br /&gt;
но мы посвятим потокам еще лишь только одну статью, в которой рассмотрим вопросы синхронизации и атрибуты потоков.&lt;/div&gt;</description>
			<pubDate>Sun, 14 Dec 2008 12:46:16 GMT</pubDate>			<dc:creator>Yaleks</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF86:Unix_API</comments>		</item>
	</channel>
</rss>