当调用pthread_exit(PTHREAD_CANCELED)时,我有预期的行为(堆栈释放,destructors调用),但线程_cancel(pthread_self())只是终止了线程。
为什么pthread_exit(PTHREAD_CANCELED)和pthread_cancel(pthread_self())有很大的不同,而且在后一种情况下线程内存没有被释放?
背景如下。
从信号处理程序中调用,这种奇怪的方法背后的原因是取消一个线程,等待外部库semop()完成(我想是在EINT上循环)。
我注意到,从其他线程调用pthread_cancel不工作(好像semop不是一个取消点),但给线程发信号然后调用pthread_exit可以工作,但会在信号处理程序中调用destructor.pthread_cancel可以将动作推迟到下一个取消点。
从具体的线程清理行为来看,通过以下方式取消一个线程应该没有区别 pthread_cancel()
并通过 pthread_exit()
.
[...]当注销被执行时,应调用线程的注销清理处理程序。当最后一个注销清理处理程序返回时,应调用线程特有的数据析构函数。当最后一个destructor函数返回时,线程应被终止。
从Linux的 man pthread_cancel
:
当取消请求被执行时,线程会发生以下步骤(按此顺序)。
取消清理处理程序被弹出(与被推送的顺序相反)并被调用。 参见pthread_cleanup_push(3)。
线程特定的数据分解器会被调用,顺序未定。 参见pthread_key_create(3)。
线程被终止。 参见pthread_exit(3))。
参照通过给线程发信号来引入取消点的策略,我怀疑这是最干净的方式。
因为很多系统调用在接收到信号时都会返回,设置了 errno
到 EINTR
在这种情况下,很容易抓住这种情况,只需让线程在这种条件下干净利落地结束自己的生命。pthread_exit()
.
所以,一些伪代码。
while (some condidtion)
{
if (-1 == semop(...))
{ /* getting here on error or signal reception */
if (EINTR == errno)
{ /* getting here on signal reception */
pthread_exit
}
}
}
结果发现没有区别 然而一些有趣的副作用发生了。
对 std::iostream
特别是cerrcout包括取消点。当底层操作被取消时,流被标记为不好。所以,如果只有一个线程在试图打印时发现了取消,你将得不到任何其他线程的输出。
所以玩 pthread_setcancelstate()
和 pthread_testcancel()
或者直接打电话 cerr.clear()
当需要的时候。
只适用于C++流,stderr,stdin似乎不受影响。
首先,有两个与线程相关联的东西会告诉你调用pthread_cancel()时该怎么做。
1. pthread_setcancelstate
2. pthread_setcanceltype
第一个函数会告诉你这个线程是否可以被取消,第二个函数会告诉你这个线程应该在什么时候以及如何被取消,例如,是在你发出取消请求后立即终止这个线程,还是需要等到这个线程达到某个里程碑后才被终止。
当你调用pthread_cancel()时,线程不会直接被终止,而是会执行以上两个动作,即检查该线程是否可以被取消,如果可以,则何时取消。
如果你禁用了取消状态,那么pthread_cancel()就不能终止该线程,但取消请求会留在队列中等待该线程成为可取消状态,也就是说,如果你启用了取消状态,那么在某个时间点,你的取消请求就会开始终止该线程。
而如果你使用pthread_exit(),那么无论线程的取消状态和取消类型如何,线程都会被终止。
*这是pthread_exit()和pthread_cancel()的区别之一,还有更多的区别。