线程取消(pthread_cancel)

线程取消机制详解
本文详细介绍了POSIX线程中的线程取消机制,包括pthread_cancel的使用方法、取消点的概念及实现、取消类型的区别,以及如何利用pthread_cleanup_push/pop进行线程终止时的资源清理。

线程取消(pthread_cancel)

基本概念
pthread_cancel调用并不等待线程终止,它只提出请求。线程在取消请求(pthread_cancel)发出后会继续运行,
直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进行动作的一个位置.


与线程取消相关的pthread函数
int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。

int pthread_setcancelstate(int state,   int *oldstate)  
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE
分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。  

int pthread_setcanceltype(int type, int *oldtype)  
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFEREDPTHREAD_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()、sem_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);
}
第2关:线程操作实战2 100 学习内容 参考答案 记录 评论 关卡排行榜 任务描述 相关知识 取消线程 编程要求 测试说明 任务描述 本关任务:编写一个小程序实现不同状态下的取消线程设置。 相关知识 为了完成本关任务,你需要掌握:取消线程的相关操作。 取消线程 (1)发起取消取消正在执行线程的操作,需要满足条件。 pthread_cancel()取消线程,调用的是取消线程清理处理程序(pthread_cleanup_push) 1)线程是否可以被取消,默认可以取消。 2)线程处于可取消点才能取消,发起取消不一定能立即取消,到取消点才可以。 (2)设置可取消状态 pthread_setcancelstate()pthread_setcanceltype()用来设置查询当前线程的可取消性状态或类型。 pthread_setcancelstate(int _state,int *_oldstate) state为要设置的新状态值; oldstate存储原来状态。 1)PTHREAD_CANCEL_DISABLE 针对目标线程取消请求将处于未决状态(未处理但存在)除非线程修改自己状态,否则不会被取消。 2)PTHREAD_CANCEL_ENABLE,针对目标线程取消请求将被传递,创建线程时是默认状态 (3)设置取消类型 pthread_setcanceltype()设置取消类型,在允许被取消线程在接收到取消操作后是立即终止还是等到取消点终止。 int pthread_setcanceltype(int _type, int *_oldtype) type调用线程新的可取消性; oldtype存储原来的类型。 PTHREAD_CANCEL_ASYNCHRONOUS,可随时执行新的或未决的取消请求。 PTHREAD_CANCEL_DEFERRED,目标达到取消点前处于未决状态(请求已发出但未处理) 在创建线程时,可取消类型设置PTHREAD_CANCEL_DEFERRED,如果禁用线程的可取消性状态,则可取消性类型设置不会立即生效,取消请求都保留为未决状态。但一旦重新启用可取消性,新类型将会生效。 成功完成pthread_setcancelstatepthread_setcanceltype将返回0,失败返回错误编号,不许设置error变量。 编程要求 根据提示,在右侧编辑器补充代码,代码思路是先开一个线程a_thread,调用thread_function函数,线程函数中先休眠1秒,然后输出一段提示,主函数休眠10秒,a_thread线程开始跑。function函数中,先将线程的可取消状态设置成DISABLE,当前无法取消线程,等待3秒输出当前线程无法取消,然后循环输出三次,每秒一次。将取消状态设置成ENABLE,可取消线程休眠200秒,返回主程序,输出取消线程,主程序将线程取消pthread_cancel ,输出等待线程完成,pthread_join等待子线程结束,释放所有缓存。请补充代码,分别将取消状态设置为DISABLEENABLE。 测试说明 平台会对你编写的代码进行测试: 代码编译 预期输出: Cancelling thread... thread cancle type is disable,can't cancle this thread Thread is running (0)... Thread is running (1)... Thread is running (2)... Now change ths canclestate is ENABLE Waiting for thread to finish...
最新发布
09-19
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值