Для регистрации обработчика следует вызвать функцию pthread_cleanup_push(), передав ей указатель на обработчик и значение его аргумента. Каждому такому вызову должен соответствовать вызов функции pthread_cleanup_pop(), которая отменяет регистрацию обработчика. Для удобства эта функция принимает дополнительный целочисленный флаг. Если он не равен нулю, при отмене регистрации выполняется операция очистки.

В листинге 4.8 показан фрагмент программы, в котором обработчик очистки применяется для удаления динамического буфера при завершении потока.

Листинг 4.8. (cleanup.c) Фрагмент программы, содержащий обработчик очистки потока

#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().

<p>4.3.2. Очистка потоковых данных в C++</p>

Программисты, работающие на C++, привыкли к тому, что очистку за них делают деструкторы объектов. Когда объект выходит за пределы своей области видимости, либо по достижении конца блока, либо вследствие возникновения исключительной ситуации, среда выполнения C++ гарантирует вызов деструкторов для тех автоматических переменных, у которых они есть. Это удобный механизм очистки, работающий независимо от того, как осуществляется выход из конкретного программного блока.

Тем не менее, если поток вызывает функцию pthread_exit(), среда выполнения C++ не может гарантировать вызов деструкторов для всех автоматических переменных, находящихся в стеке потока. Чтобы этого добиться, нужно вызвать функцию pthread_exit() в рамках конструкции try/catch, охватывающей все тело потоковой функции. При этом перехватывается специальное исключение ThreadExitException.

Программа, приведенная в листинге 4.9, иллюстрирует данную методику. Потоковая функция сообщает о своем намерении завершить поток, генерируя исключение ThreadExitException, а не вызывая функцию pthread_exit() явно. Поскольку исключение перехватывается на самом верхнем уровне потоковой функции, все локальные переменные, находящиеся в стеке потока, будут удалены правильно.

Листинг 4.9. (cxx-exit.cpp) Безопасное завершение потока в C++

#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())

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

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