1、pthread_cancel(pthread_t tid);
功能描述:向线程号为tid的线程发送一个取消运行的请求。线程tid是不是真的会被取消?何时会被取消?这两个问题依赖于本线程的两个控制属性:是否取消依赖于一个状态使能标志stat、何时取消依赖于一个类型标志type。
pthread_setcancelstate函数可以设置状态为使能enable或者不使能disable(一个线程创建后的默认状态为使能)。如果设置为非使能,那么pthread_cancel发出的取消请求,仍然会被插入请求队列,一旦stat标志被改为使能,线程仍可以收到这个取消请求;如果stat设置为使能,那么pthread_cancel发出的取消请求会立即被线程接收到。收到取消请求以后,何时执行取消,这就得继续看type属性了。
pthread_setcanceltype函数可以设置线程的type属性,可设置的值为:异步取消asynchronous、延迟取消deferred(一个线程创建后的默认type为deferred)。异步取消,指的是只要执行了pthread_cancel函数,那么本线程就应该会被立即取消(“应该”这个词的意思是,内核并不能100%保证能够立即取消,但是内核会尽快的取消它)。如果type被设置为defered,那么本线程将会在下一个取消点被取消。下面这几个函数被称为取消点:
pthread_join()、 pthread_testcancel()、pthread_cond_wait()、 pthread_cond_timedwait()、sem_wait()、sigwait()。
当取消请求开始响应之后,内核会自动执行下面的步骤:
① pthread_cleanup_push压入的函数将会被倒序执行一遍;
② 调用线程数据的析构函数(更多细节请参考pthread_key_create函数)
③ 线程真正结束(更多细节请参考pthread_exit函数)
使用pthread_join函数可以读取到线程的退出状态为:PTHREAD_CANCELED。
返回值:成功返回0,失败返回非0
2、void pthread_cleanup_push(void (*routine)(void *), void *arg);
3、void pthread_cleanup_pop(int execute);
这两个函数在linux中是用含左右{}的宏实现的,所以,这两个函数必须成对出现,且必须处于同一级别的代码中。
功能描述:注册一些函数routine到一个队列中,当线程被取消后,这些函数会按照被注册的相反顺序,以本函数的形参arg作为实参,被执行一遍。注册的这些函数有啥作用?一个典型的应用方法比如:解锁一个互斥锁。
本函数的亲兄弟函数pthread_cleanup_pop,会把注册到队列中的函数,处在队列最前面的那个给删掉,至于删掉后,是否要执行被删的这个函数,依赖于pthread_cleanup_pop的实参execute,0代表删掉后不执行,非0代表删掉后要执行一次。
当线程执行pthread_cancel后,且真正开始响应取消请求后,会发生下述动作:
① 当按照队列中注册的相反顺序,把注册的函数带arg参数执行一遍;
② 如果用pthread_exit函数来终止线程,那么队列中注册的函数也会被执行;如果在线程的启动函数中,用return语句来终止线程,那么这些注册的函数将不会被执行!
③ 当调用pthread_cleanup_pop函数,且带非0实参,那么最后注册的一个函数会从队列中被移除,然后被立即执行一次。
典型应用1:https://blog.csdn.net/caianye/article/details/5912172
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);
本来do some work之后是有pthread_mutex_unlock(&mut);这句,也就是有解锁操作,但是在do some work时会出现非正常终止,那样的话,系统会根据pthread_cleanup_push中提供的函数,和参数进行解锁操作或者其他操作,以免本线程未解锁mutex而导致的其他线程永远得不到mutex。
典型应用2:
还有一种应用是释放动态申请的内存,
void my_fun(void *arg)
{
free(arg);
}
void *thread1(void *arg)
{
char *pImage = malloc(1M);
pthread_cleanup_push(my_fun, pImage);
....//其他代码
free(pImage );
pthread_cleanup_pop(0);
return NULL;
}
分析:我们在线程中动态申请了大块内存,如果还没到free语句,线程意外退出了(例如被cancel了),那么这块内存将无法被释放,因此我们提前注册一个内存释放函数my_fun,只要线程在pthread_cleanup_pop(0)语句前意外退出了,那么my_fun函数就会被执行;只要执行到了pthread_cleanup_pop(0)这一语句,那么my_func函数会被出栈,这时在线程退出后,my_func也不会被执行了。
另外,我们也可以省略掉thread1函数中的free语句,然后把pthread_cleanup_pop(0)改为pthread_cleanup_pop(1)。这样做也可以释放掉mallc的内存,原因在于pthread_cleanup_pop的实参为非0值,这样my_func出栈时,会被执行一次。(所谓出栈,本质上说其实就是把my_func函数从清理队列中删掉)