Листинг 32.1. Отмена потока с помощью pthread_cancel()
threads/thread_cancel.c
#include
#include "tlpi_hdr.h"
static void *
threadFunc(void *arg)
{
int j;
printf("New thread started\n"); /* Может быть точкой отмены */
for (j = 1;; j++) {
printf("Loop %d\n", j); /* Может быть точкой отмены */
sleep(1); /* Точка отмены */
}
/* NOTREACHED */
return NULL;
}
int
main(int argc, char *argv[])
{
pthread_t thr;
int s;
void *res;
s = pthread_create(&thr, NULL, threadFunc, NULL);
if (s!= 0)
errExitEN(s, "pthread_create");
sleep(3); /* Позволяем новому потоку проработать некоторое время */
s = pthread_cancel(thr);
if (s!= 0)
errExitEN(s, "pthread_cancel");
s = pthread_join(thr, &res);
if (s!= 0)
errExitEN(s, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("Thread was canceled\n");
else
printf("Thread was not canceled (should not happen!)\n");
exit(EXIT_SUCCESS);
}
threads/thread_cancel.c
В листинге 32.1 поток, созданный функцией main(), принял запрос отмены, поскольку он выполнял функцию, которая является точкой отмены (sleep() является ею наверняка, а printf() может быть таковой). Но представьте, что поток выполняет цикл, который не содержит точек отмены (например, состоящий из сплошных вычислений). В этом случае запрос отмены никогда не будет удовлетворен.
Единственное назначение функции pthread_testcancel() — быть точкой отмены. Если отмена отложена во время вызова данной функции, это означает, что вызывающий поток завершен.
#include
void pthread_testcancel(void);
Поток, который не содержит точек отмены, может время от времени вызывать pthread_testcancel(), чтобы обеспечить своевременный ответ на запрос отмены, отправляемый другим потоком.
Если поток с отложенной отменой просто завершается при достижении точки отмены, разделяемые переменные и объекты библиотеки Pthreads (например, мьютексы) могут остаться в несогласованном состоянии, что может привести к получению некорректных результатов, взаимным блокировкам или аварийному завершению оставшихся потоков. Чтобы обойти эту проблему, поток может установить один или несколько
Каждый поток может иметь стек обработчиков для очистки ресурсов. При отмене потока эти обработчики выполняются снизу вверх; то есть обработчик, установленный позже других, вызывается первым. Когда все обработчики выполнились, поток завершается.
Функции pthread_cleanup_push() и pthread_cleanup_pop() соответственно добавляют и удаляют обработчики в стеке вызывающего потока.
#include
void pthread_cleanup_push(void (*
void pthread_cleanup_pop(int
Вызов pthread_cleanup_push() добавляет функцию, чей адрес указан в аргументе routine, на вершину стека обработчиков для очистки ресурсов потока. Аргумент routine указывает на функцию следующего вида:
void
routine(void *arg)
{
/* Код для очистки ресурсов */
}
Значение arg, переданное в pthread_cleanup_push(), предоставляется в виде аргумента вызываемого обработчика. Тип этого аргумента — void *, но его можно привести и к другому типу данных.
Обычно очистка требуется, только если поток был отменен во время выполнения определенного участка кода. Если поток проходит этот участок без отмены, очищать ресурсы больше не нужно. В связи с этим pthread_cleanup_push() имеет сопровождающий вызов, pthread_cleanup_pop(), который удаляет функцию на вершине стека обработчиков для очистки ресурсов. Если аргумент execute не равен 0, обработчик тоже выполняется. Это может пригодиться в ситуациях, когда нам нужно очистить ресурсы, даже если поток не был отменен.
Здесь мы описываем pthread_cleanup_push() и pthread_cleanup_pop() как функции, но стандарт SUSv3 позволяет реализовывать их в виде макросов, которые разворачиваются в цепочки инструкций, включающих соответственно открывающую ({) и закрывающую (}) скобки. Так происходит в Linux и многих других системах, хотя не все реализации UNIX используют данный подход. Это означает, что в одном и том же лексическом блоке кода каждому pthread_cleanup_push() должен соответствовать pthread_cleanup_pop() (в системах, которые реализованы таким образом, переменные, объявленные между вызовами pthread_cleanup_push() и pthread_cleanup_pop(), будут ограничены этой лексической областью). Например, следующий код является некорректным:
pthread_cleanup_push(func, arg);
…
if (cond) {
pthread_cleanup_pop(0);
}