线程取消(pthread_cancel)
基本概念
pthread_cancel调⽤并不等待线程终⽌,它只提出请求。线程在取消请求(pthread_cancel)发出后会继续运⾏,
直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进⾏动作的⼀个位置.
与线程取消相关的pthread函数
int pthread_cancel(pthread_t thread)
发送终⽌信号给thread线程,如果成功则返回0,否则为⾮0值。发送成功并不意味着thread会终⽌。
int pthread_tcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,
分别表⽰收到信号后设为CANCLED状态和忽略CANCEL信号继续运⾏;old_state如果不为NULL则存⼊原来的Cancel状态以便恢复。
int pthread_tcanceltype(int type, int *oldtype)
设置本线程取消动作的执⾏时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表⽰收到信号后继续运⾏⾄下⼀个取消点再退出和⽴即执⾏取消动作(退出);oldtype如果不为NULL则存⼊运来的取消动作类型值。
void pthread_testcancel(void)
是说pthread_testcancel在不包含取消点,但是⼜需要取消点的地⽅创建⼀个取消点,以便在⼀个没有包含取消点的执⾏代码线程中响应取消请求.
线程取消功能处于启⽤状态且取消状态设置为延迟状态时,pthread_testcancel()函数有效。
如果在取消功能处处于禁⽤状态下调⽤pthread_testcancel(),则该函数不起作⽤。
无人知是荔枝来
请务必仅在线程取消线程操作安全的序列中插⼊pthread_testcancel()。除通过pthread_testcancel()调⽤以编程⽅式建⽴的取消点意
外,pthread标准还指定了⼏个取消点。测试退出点,就是测试cancel信号.
取消点:
线程取消的⽅法是向⽬标线程发Cancel信号,但如何处理Cancel信号则由⽬标线程⾃⼰决定,或者忽略、或者⽴即终⽌、或者继续运⾏⾄Cancelation-point(取消点),由不同的Cancelation状态决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运⾏⾄取消点,也就是说设置⼀个CANCELED状态,线程继续运⾏,只有运⾏⾄Cancelation-point的时候才会退出。
pthreads标准指定了⼏个取消点,其中包括:
(1)通过pthread_testcancel调⽤以编程⽅式建⽴线程取消点。
(2)线程等待pthread_cond_wait或pthread_cond_timewait()中的特定条件。
(3)被sigwait(2)阻塞的函数
(4)⼀些标准的库调⽤。通常,这些调⽤包括线程可基于阻塞的函数。
缺省情况下,将启⽤取消功能。有时,您可能希望应⽤程序禁⽤取消功能。如果禁⽤取消功能,则会导致延迟所有的取消请求,
直到再次启⽤取消请求。
根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、m_wait()、sigwait()等函数以及
read()、write()等会引起阻塞的系统调⽤都是Cancelation-point,⽽其他pthread函数都不会引起Cancelation动作。
但是pthread_cancel的⼿册页声称,由于LinuxThread库与C库结合得不好,因⽽⽬前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调⽤中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调⽤前后调⽤pthread_testcancel(),从⽽达到POSIX标准所要求的⽬标.
即如下代码段:
宝宝宁愿饿也不吃奶粉pthread_testcancel();
retcode = read(fd, buffer, length);
pthread_testcancel();
注意:
程序设计⽅⾯的考虑,如果线程处于⽆限循环中,且循环体内没有执⾏⾄取消点的必然路径,则线程⽆法由外部其他线程的取消请求⽽终⽌。因此在这样的循环体的必经路径上应该加⼊pthread_testcancel()调⽤.车文明
动物背景图片
取消类型(Cancellation Type)济南大学分数线
我们会发现,通常的说法:某某函数是 Cancellation Points,这种⽅法是容易令⼈混淆的。
因为函数的执⾏是⼀个时间过程,⽽不是⼀个时间点。其实真正的 Cancellation Points 只是在这些函数中 Cancellation Type 被修改为PHREAD_CANCEL_ASYNCHRONOUS 和修改回 PTHREAD_CANCEL_DEFERRED 中间的⼀段时间。
POSIX的取消类型有两种,⼀种是延迟取消(PTHREAD_CANCEL_DEFERRED),这是系统默认的取消类型,即在线程到达取消点之前,不会出现真正的取消;另外⼀种是异步取消(PHREAD_CANCEL_ASYNCHRONOUS),使⽤异步取消时,线程可以在任意时间取消。
线程终⽌的清理⼯作
Posix的线程终⽌有两种情况:正常终⽌和⾮正常终⽌。
线程主动调⽤pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出⽅式;
⾮正常终⽌是线程在其他线程的⼲预下,或者由于⾃⾝运⾏出错(⽐如访问⾮法地址)⽽退出,这种退出⽅式是不可预见的。
不论是可预见的线程终⽌还是异常终⽌,都会存在资源释放的问题,在不考虑因运⾏出错⽽退出的前提下,如何保证线程终⽌时能顺利的释放掉⾃⼰所占⽤的资源,特别是锁资源,就是⼀个必须考虑解决的问题。
最经常出现的情形是资源独占锁的使⽤:线程为了访问临界资源⽽为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采⽤异步⽅式响应,或者在打开独占锁以前的运⾏路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要⼀个机制来简化⽤于资源释放的编程。
在POSIX线程API中提供了⼀个pthread_cleanup_push()/ pthread_cleanup_pop()函数,
对⽤于⾃动释放资源—从pthread_cleanup_push()的调⽤点到pthread_cleanup_pop()之间的程序段中的终⽌动作(包括调⽤pthread_exit()和取消点终⽌)都将执⾏pthread_cleanup_push()所指定的清理
函数。
逢雪宿芙蓉山主人API定义如下:
void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)
pthread_cleanup_push()/pthread_cleanup_pop()采⽤先⼊后出的栈结构管理,void routine(void *arg)函数
在调⽤pthread_cleanup_push()时压⼊清理函数栈,多次对pthread_cleanup_push() 的调⽤将在清理函数栈中形成⼀个函数链;
从pthread_cleanup_push的调⽤点到pthread_cleanup_pop之间的程序段中的终⽌动作(包括调⽤pthread_exit()和异常终⽌,不包括return)
都将执⾏pthread_cleanup_push()所指定的清理函数。
在执⾏该函数链时按照压栈的相反顺序弹出。execute参数表⽰执⾏到 pthread_cleanup_pop()时
按摩屁股
是否在弹出清理函数的同时执⾏该函数,为0表⽰不执⾏,⾮0为执⾏;这个参数并不影响异常终⽌时清理函数的执⾏。
pthread_cleanup_push()/pthread_cleanup_pop()是以宏⽅式实现的,这是pthread.h中的宏定义:
#define pthread_cleanup_push(routine,arg) \
{
struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop (&_buffer, (execute)); \
}
可见,pthread_cleanup_push()带有⼀个"{",⽽pthread_cleanup_pop()带有⼀个"}",因此这两个函数必须成对出现,且必须位于程序的同⼀级别的代码段中才能通过编译。
在下⾯的例⼦⾥,当线程在"do some work"中终⽌时,将主动调⽤pthread_mutex_unlock(mut),以完成解锁动作。
pthread_cleanup_push(pthread_mutex_unlock, (void*) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);
或者
void cleanup(void *arg)
{
pthread_mutex_unlock(&mutex);
}
每日一签免费
void* thread0(void* arg)
{
pthread_cleanup_push(cleanup, NULL); // thread cleanup handler p
thread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);
pthread_exit(NULL);
}