linux多线程相关的API-(3)--线程取消cancel与清理push/pop

本文详细介绍了pthread中的线程取消机制,包括如何通过pthread_cancel发送取消请求、如何设置取消状态及类型,以及如何利用pthread_cleanup_push和pthread_cleanup_pop进行资源清理。通过示例展示了在不同情况下线程取消的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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函数从清理队列中删掉)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值