Поток, «позволивший» себя аннулировать, прежде чем завершиться, обычно должен выполнить некоторые заключительные действия. Так, нужно закрыть файлы, привести разделяемые данные в надлежащее состояние, снять блокировки или освободить занимаемые ресурсы. Библиотека Pthread определяет механизм поведения каждого потока «в последние минуты своей жизни». С каждым потоком связывается стек очистительно-восстановительных операций (cleanup stack), который содержит указатели на процедуры (или функции), предназначенные для выполнения во время аннулирования потока. Для того чтобы поместить в этот стек указатель на процедуру, предусмотрена функция pthread_cleanup_push ().
Синопсис
#include
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread cleanup pop(int execute);
Параметр routine представляет собой указатель на функцию, помещаемый в стек завершающих процедур. Параметр arg содержит аргумент, передаваемый этой routine -функции, которая вызывается при завершении потока с помощью функции pthread_exit (), когда поток «покоряется» запросу на аннулирование или явным образом вызывает функцию pthread__cleanup_pop () с ненулевым значением параметра execute. Функция, заданная параметром routine, не возвращает никакого значения.
Функция pthread_cleanup_pop() удаляет указатель routine -функции из вершины стека завершающих процедур вызывающего потока. Параметр execute может принимать значение 1 или 0. Если его значение равно 1, поток выполняет routine- функцию, даже если он при этом и не завершается. Поток продолжает свое выполнение с инструкции, расположенной за вызовом функции pthread_cleanup_pop(). Если значение параметра execute равно 0, указатель извлекается из вершины стека потока без выполнения routine -функции.
Необходимо позаботиться о том, чтобы для каждой функции занесения в стек (push) существовала функция извлечения из стека (pop) в пределах одной и той же лексической области видимости. Например, для функции funcA () обязательно выполнение cleanup -обработчика при ее нормальном завершении или аннулировании:
void *funcA(void *X)
{
int *Tid;
Tid = new int;
// do some work
//...
pthread_cleanup_push(cleanup_funcA,Tid);
// do some more work
//...
pthread_cleanup_pop(0);
}
Здесь функция funcA( ) помещает указатель на обработчик cleanup_funcA( ) в стек завершающих процедур путем вызова функции pthread_cleanup_push (). Каждому обращению к этой функции должно соответствовать обращение к функции pthread_cleanup_pop(). Если функции извлечения указателя из стека (pop- функции) передается значение 0, то извлечение из стека состоится, но без выполнения обработчика. Обработчик будет выполнен лишь при аннулировании потока, выполняющего функцию funcA().
Для функции funcB () также требуется cleanup -обработчик:
void *funcB(void *X)
{
int *Tid;
Tid = new int;
// do some work
//...
pthread_cleanup_push(cleanup_funcB,Tid);
// do some more work
//...
pthread_cleanup_pop(1);
}
Здесь функция funcB () помещает указатель на обработчик cleanup_funcB () в стек завершающих процедур. Отличие этого примера от предыдущего состоит в том, что функции pthread_cleanup_pop () передается параметр со значением 1, т.е. после извлечения из стека указателя на обработчик этот обработчик будет тут же выполнен. Необходимо отметить, что выполнение обработчика в данном случае состоится «при любой погоде», т.е. и при аннулировании потока, который обеспечивает выполнение функции funcB( ), и при обычном его завершении. Обработчики- «уборщики», cleanup_funcA() и cleanup_funcB (), — это обычные функции, которые можно использовать для закрытия файлов, освобождения ресурсов, разблокирования мьютексов и пр.
Управление стеком потока