99-再议 pthread_cancel
2017-03-17 14:01
393 查看
在线程创建与终止中就曾提到过 pthread_cancel 函数,它是用来终止指定的线程的,就好像线程自己调用了 pthread_exit(PTHREAD_CANCEL) 一样。不过,在那个时候并未讨论调用 pthread_cancel 后线程什么时候终止。
本文主要目的就是验证 pthread_cancel 的退出时机。
下面的几个程序,在线程中编写了计算密集型程序(一个很大的循环),循环执行完毕后,打印该循环消耗的时间。接下来,又打印了一行。
如果 cannot be canceled 打印出来,表明线程并未被取消。
图1 实验 1 结果
从图 1 中可以看到,当主线程发出取消请求后,线程并未立即停止,而是一直等到打印了 finished, consume 4 s 才停止。
当执行第一个 printf 函数时,线程退出了,说明在 printf 的调用链中,肯定有一个步骤调用了取消点函数。实际上,printf 在底层是调用了 write 接口,write 函数中存在取消点。
有很多函数都有取消点,具体可参考 apue 一书中的第 12 章——线程控制。
如果一直没有机会调用包含取消点的函数,那就意味着线程即使收到了“取消信号”也不会退出。这时候可以显示的在线程内部调用
其中,type 的值如下:
默认情况下,线程取消方式为默认值——
图2 异步取消
从图 2 可以看到,printf 函数还未执行,或者说还没遇到取消点,线程就退出了。
这时候,可以在线程函数中禁止取消。下面的函数可以实现此功能:
其中,state 可以为下面的值:
默认情况下,state 的值为
图3 将线程设置为禁用取消状态
从图 3 中可以看到,将线程设置为
掌握异步取消
掌握禁用取消
本文主要目的就是验证 pthread_cancel 的退出时机。
下面的几个程序,在线程中编写了计算密集型程序(一个很大的循环),循环执行完毕后,打印该循环消耗的时间。接下来,又打印了一行。
如果 cannot be canceled 打印出来,表明线程并未被取消。
1. 实验
1.1 代码
// cancel.c #include <stdio.h> #include <pthread.h> #include <time.h> void* fun(void *arg) { int i, j, start; start = time(0); for (i = 0; i < 1000; ++i) { for (j = 0; j < 1000000; ++j); } printf("finished, consume %ld s\n", time(0) - start); printf("cannot be canceled\n"); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, fun, NULL); pthread_cancel(tid); puts("cancel thread"); pthread_join(tid, NULL); return 0; }
1.2 运行结果及分析
图1 实验 1 结果
从图 1 中可以看到,当主线程发出取消请求后,线程并未立即停止,而是一直等到打印了 finished, consume 4 s 才停止。
1.3 取消点
出现图 1 中的结果,原因在于 pthread_cancel 在默认情况下,只是通知线程,至于线程什么时候会取消,只有在遇到了取消点(把它想象成某个函数)的时候才会停止。在密集计算的循环中,并没有调用任何取消点函数。当执行第一个 printf 函数时,线程退出了,说明在 printf 的调用链中,肯定有一个步骤调用了取消点函数。实际上,printf 在底层是调用了 write 接口,write 函数中存在取消点。
有很多函数都有取消点,具体可参考 apue 一书中的第 12 章——线程控制。
如果一直没有机会调用包含取消点的函数,那就意味着线程即使收到了“取消信号”也不会退出。这时候可以显示的在线程内部调用
void pthread_testcancel(void)进行测试,如果真的收到了“取消信号”,则退出。
2. 默认取消和异步取消
有一种办法,可以让线程还没到达取消点的时候直接退出。这时候需要将线程设置为异步取消的方式。具体方法为在线程内部调用下面的函数:int pthread_setcanceltype(int type, int *oldtype);
其中,type 的值如下:
PTHREAD_CANCEL_ASYNCHRONOUS
PTHREAD_CANCEL_DEFERRED
默认情况下,线程取消方式为默认值——
PTHREAD_CANCEL_DEFERRED。要想让线程收到“取消信号”后立即退出,需要将 type 设置为
PTHREAD_CANCEL_ASYNCHRONOUS.
2.1 代码
// asyncancel.c #include <stdio.h> #include <pthread.h> #include <time.h> void* fun(void *arg) { int i, j, start, oldtype; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); start = time(0); for (i = 0; i < 1000; ++i) { for (j = 0; j < 1000000; ++j); } printf("finished, consume %ld s\n", time(0) - start); printf("cannot be canceled\n"); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, fun, NULL); sleep(1); pthread_cancel(tid); puts("cancel thread"); pthread_join(tid, NULL); return 0; }
2.2 运行结果
图2 异步取消
从图 2 可以看到,printf 函数还未执行,或者说还没遇到取消点,线程就退出了。
3. 禁止取消
有些线程不是你想取消就取消的。就算你发送了 pthread_cancel 通知了,它也不会退出。除非它自己想退出了。这时候,可以在线程函数中禁止取消。下面的函数可以实现此功能:
int pthread_setcancelstate(int state, int *oldstate);
其中,state 可以为下面的值:
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
默认情况下,state 的值为
PTHREAD_CANCEL_ENABLE,即可以被取消。如果将 state 设置为
PTHREAD_CANCEL_DISABLE,则线程是无法被取消的。
3.1 代码
// disablecancel.c #include <stdio.h> #include <pthread.h> #include <time.h> void* fun(void *arg) { int i, j, start, oldstate; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); start = time(0); for (i = 0; i < 1000; ++i) { for (j = 0; j < 1000000; ++j); } printf("finished, consume %ld s\n", time(0) - start); printf("cannot be canceled\n"); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, fun, NULL); pthread_cancel(tid); puts("cancel thread"); pthread_join(tid, NULL); return 0; }
3.2 运行结果
图3 将线程设置为禁用取消状态
从图 3 中可以看到,将线程设置为
PTHREAD_CANCEL_DISABLE后,线程是无法被取消的。
4. 总结
理解取消点的概念掌握异步取消
掌握禁用取消
相关文章推荐
- 线程取消(pthread_cancel)
- 线程取消(pthread_cancel)
- 线程正常终止pthread_exit,pthread_join,pthread_kill,pthread_cancel,sigwait,sigaddset
- pthread_kill和pthread_cancel
- 6、一个 pthread_cancel 引起的线程死锁(转)
- pthread_cancel手册
- 2.pthread_join()、pthread_exit()、pthread_cancel()简述
- 线程取消(pthread_cancel)
- linux多线程---pthread_cancel
- pthread_cancel 线程取消以及锁的释放
- 一个 pthread_cancel 引起的线程死锁【整理转载】
- 【那些年遇到过的面试题】pthread_cancel
- Linux--多线程之线程的取消pthread_cancel
- 6、一个 pthread_cancel 引起的线程死锁【整理转载】
- 调用系统函数pthread_cancel取消进程的其他线程
- android ndk not support pthread_cancel
- pthread_cancel,pthread_killall 段错误
- err=pthread_cancel(tid1);
- pthread_cancel 进程和线程的讲解
- pthread_cancel与取消点----笛风读书笔记系列