Читаем Linux API. Исчерпывающее руководство онлайн бесплатно
В качестве дополнительного удобства все обработчики для очистки ресурсов, которые не сработали, выполняются автоматически, если поток завершается с помощью вызова pthread_exit() (это не касается ситуаций, когда для завершения используется инструкция return).
В листинге 32.2 представлен простой пример использования обработчика для освобождения ресурсов. Главная программа создает поток , который первым делом выделяет блок памяти
(его адрес будет храниться в переменной buf) и затем закрывает мьютекс mtx
. Поскольку поток может быть отменен, он применяет функцию pthread_cleanup_push()
для установки обработчика очистки ресурсов, который вызывается по адресу, хранящемуся внутри buf. Если выполнить этот обработчик, он очистит освободившуюся память
и откроет мьютекс
.
Затем поток входит в цикл и ждет, когда условная переменная cond получит уведомление . Этот цикл будет завершен одним из двух способов в зависимости от того, запускалась ли программа с аргументом командной строки.
• Если аргумент командной строки отсутствует, поток отменяется функцией main() . В этом случае отмена произойдет в момент вызова функции pthread_cond_wait()
, которая является точкой отмены (см. табл. 32.1). В ходе этой процедуры автоматически выполняются обработчики очистки ресурсов, установленные с помощью pthread_cleanup_push().
• Если аргумент командной строки был предоставлен, условная переменная получает уведомление после того, как связанной с ней глобальной переменной glob будет впервые присвоено ненулевое значение. В этом случае поток проходит через выполнение функции pthread_cleanup_pop()
, которая, если ей передать ненулевой аргумент, тоже приводит к вызову обработчика очистки ресурсов.
Главная программа присоединяет завершенный поток и сообщает, был ли он отменен или завершился в штатном режиме.
Листинг 32.2. Использование обработчиков для освобождения ресурсов
threads/thread_cleanup.c
#include
#include "tlpi_hdr.h"
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static int glob = 0; /* Предикат */
static void /* Освобождаем память по адресу 'arg' и открываем мьютекс */
cleanupHandler(void *arg)
{
int s;
printf("cleanup: freeing block at %p\n", arg);
free(arg);
printf("cleanup: unlocking mutex\n");
s = pthread_mutex_unlock(&mtx);
if (s!= 0)
errExitEN(s, "pthread_mutex_unlock");
}
static void *
threadFunc(void *arg)
{
int s;
void *buf = NULL; /* Буфер, выделенный потоком */
buf = malloc(0x10000); /* Не является точкой отмены */
printf("thread: allocated memory at %p\n", buf);
s = pthread_mutex_lock(&mtx); /* Не является точкой отмены */
if (s!= 0)
errExitEN(s, "pthread_mutex_lock");
pthread_cleanup_push(cleanupHandler, buf);
while (glob == 0) {
s = pthread_cond_wait(&cond, &mtx); /* Точка отмены */
if (s!= 0)
errExitEN(s, "pthread_cond_wait");
}
printf("thread: condition wait loop completed\n");
pthread_cleanup_pop(1); /* Выполняет обработчик очистки ресурсов */
return NULL;
}
int
main(int argc, char *argv[])
{
pthread_t thr;
void *res;
int s;
s = pthread_create(&thr, NULL, threadFunc, NULL);
if (s!= 0)
errExitEN(s, "pthread_create");
sleep(2); /* Даем потоку шанс начать работу */
if (argc == 1) { /* Отменяем поток */
printf("main: about to cancel thread\n");
s = pthread_cancel(thr);
if (s!= 0)
errExitEN(s, "pthread_cancel");
} else { /* Уведомляем условную переменную */
printf("main: about to signal condition variable\n");
glob = 1;
s = pthread_cond_signal(&cond);
if (s!= 0)
errExitEN(s, "pthread_cond_signal");
}
s = pthread_join(thr, &res);
if (s!= 0)
errExitEN(s, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("main: thread was canceled\n");
else
printf("main: thread terminated normally\n");
exit(EXIT_SUCCESS);
}
threads/thread_cleanup.c
Если запустить программу из листинга 32.2 без аргументов командной строки, функция main() сделает вызов pthread_cancel(), после чего автоматически выполнится обработчик очистки ресурсов, и мы увидим следующее:
$ ./thread_cleanup
thread: allocated memory at 0x804b050
main: about to cancel thread
cleanup: freeing block at 0x804b050
cleanup: unlocking mutex
main: thread was canceled
Если запустить программу с аргументом командной строки, функция main() присвоит glob значение 1 и уведомит об этом условную переменную, после чего вызов pthread_cleanup_pop() приведет к выполнению обработчика очистки ресурсов, и мы увидим следующее:
$ ./thread_cleanup s
thread: allocated memory at 0x804b050
main: about to signal condition variable
thread: condition wait loop completed
cleanup: freeing block at 0x804b050
cleanup: unlocking mutex
main: thread terminated normally
Если сделать поток асинхронно отменяемым (тип отмены PTHREAD_CANCEL_ASYNCHRONOUS), его можно будет отменить в любой момент (то есть в любом машинном коде); это процедура не задерживается до достижения потоком следующей точки отмены.




