В качестве дополнительного удобства все обработчики для очистки ресурсов, которые не сработали, выполняются автоматически, если поток завершается с помощью вызова 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

32.6. Асинхронная отмена

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

Перейти на страницу:

Похожие книги