Для регистрации обработчика следует вызвать функцию pthread_cleanup_push()
, передав ей указатель на обработчик и значение его аргумента. Каждому такому вызову должен соответствовать вызов функции pthread_cleanup_pop()
, которая отменяет регистрацию обработчика. Для удобства эта функция принимает дополнительный целочисленный флаг. Если он не равен нулю, при отмене регистрации выполняется операция очистки.
В листинге 4.8 показан фрагмент программы, в котором обработчик очистки применяется для удаления динамического буфера при завершении потока.
#include
#include
/* Выделение временного буфера. */
void* allocate_buffer(size_t size) {
return malloc(size);
}
/* Удаление временного буфера. */
void deallocate_buffer(void* buffer) {
free(buffer);
}
void do_some_work() {
/* Выделение временного буфера. */
void* temp_buffer = allocate_buffer(1024);
/* Регистрация обработчика очистки для данного буфера. Этот
обработчик будет удалять буфер при завершении или отмене
потока. */
pthread_cleanup_push(deallocate_buffer, temp_buffer);
/* Выполнение других действий... */
/* Отмена регистрации обработчика. Поскольку функции передается
ненулевой аргумент, она выполняет очистку, вызывая функцию
deallocate_buffer(). */
pthread_cleanup_pop(1);
}
В данном случае функции pthread_cleanup_pop()
передается ненулевой аргумент, поэтому функция очистки deallocate_buffer()
вызывается автоматически. В данном простейшем случае можно было в качестве обработчика непосредственно использовать стандартную библиотечную функцию free()
.
Программисты, работающие на C++, привыкли к тому, что очистку за них делают деструкторы объектов. Когда объект выходит за пределы своей области видимости, либо по достижении конца блока, либо вследствие возникновения исключительной ситуации, среда выполнения C++ гарантирует вызов деструкторов для тех автоматических переменных, у которых они есть. Это удобный механизм очистки, работающий независимо от того, как осуществляется выход из конкретного программного блока.
Тем не менее, если поток вызывает функцию pthread_exit()
, среда выполнения C++ не может гарантировать вызов деструкторов для всех автоматических переменных, находящихся в стеке потока. Чтобы этого добиться, нужно вызвать функцию pthread_exit()
в рамках конструкции try/catch
, охватывающей все тело потоковой функции. При этом перехватывается специальное исключение ThreadExitException
.
Программа, приведенная в листинге 4.9, иллюстрирует данную методику. Потоковая функция сообщает о своем намерении завершить поток, генерируя исключение ThreadExitException
, а не вызывая функцию pthread_exit()
явно. Поскольку исключение перехватывается на самом верхнем уровне потоковой функции, все локальные переменные, находящиеся в стеке потока, будут удалены правильно.
#include
class ThreadExitException {
public:
/* Конструктор, принимающий аргумент RETURN_VALUE, в котором
содержится возвращаемое потоком значение. */
ThreadExitException(void* return_value) :
thread_return_value_(return_value) {
}
/* Реальное завершение потока. В программу возвращается
значение, переданное конструктору. */
void* DoThreadExit() {
pthread_exit(thread_return_value_);
}
private:
/* Значение, возвращаемое в программу при завершении потока. */
void* thread_return_value_;
};
void do_some_work() {
while (1) {
/* Здесь выполняются основные действия... */
if (should_exit_thread_immediately())