线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
(1)什么是线程取消点
根据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);
(2)与线程取消相关的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_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。
void pthread_testcancel(void)
Calling pthread_testcancel() creates a cancellation point within the callingthread, so that a thread that is otherwise executing code that contains nocancellation points will respond to a cancellation request.
If cancelability is disabled (using pthread_setcancelstate(3)), or nocancellation request is pending, then a call to pthread_cancel(3) has noeffect.
也就是说pthread_testcancel在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求。
示例代码【3】
View Code
#include <pthread.h>#include <sys/types.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#define handle_error_en(en, msg) do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)static int done = 0;static int cleanup_pop_arg = 0;static int cnt = 0;static voidcleanup_handler(void *arg){ printf("Called clean-up handlern"); cnt = 0;}static void *thread_start(void *arg){ time_t start, curr; printf("New thread startedn"); pthread_cleanup_push(cleanup_handler, NULL); curr = start = time(NULL); while (!done) { pthread_testcancel(); /* A cancellation point */ if (curr < time(NULL)) { curr = time(NULL); printf("cnt = %dn", cnt); /* A cancellation point */ cnt++; } } pthread_cleanup_pop(cleanup_pop_arg); return NULL;}intmain(int argc, char *argv[]){ pthread_t thr; int s; void *res; s = pthread_create(&thr, NULL, thread_start, NULL); if (s != 0) handle_error_en(s, "pthread_create"); sleep(2); /* Allow new thread to run a while */ if (argc > 1) { if (argc > 2) cleanup_pop_arg = atoi(argv[2]); done = 1; } else { printf("Canceling threadn"); s = pthread_cancel(thr); if (s != 0) handle_error_en(s, "pthread_cancel"); } s = pthread_join(thr, &res); if (s != 0) handle_error_en(s, "pthread_join"); if (res == PTHREAD_CANCELED) printf("Thread was canceled; cnt = %dn", cnt); else printf("Thread terminated normally; cnt = %dn", cnt); exit(EXIT_SUCCESS);}
示例代码跟踪
View Code
[root@localhost CFunctionTest]# ./threadNew thread startedcnt = 0cnt = 1Canceling threadCalled clean-up handlerThread was canceled; cnt = 0[root@localhost CFunctionTest]# gdb thread(gdb) b 18//...(gdb) rStarting program: /home/***/a***/CFunctionTest/thread [Thread debugging using libthread_db enabled]Breakpoint 6, main (argc=1, argv=0xbffff374) at testthread.cpp:5454 s = pthread_create(&thr, NULL, thread_start, NULL);Missing separate debuginfos, use: debuginfo-install glibc-2.12.90-17.i686 libgcc-4.5.1-4.fc14.i686 libstdc++-4.5.1-4.fc14.i686(gdb) n[New Thread 0xb7fecb70 (LWP 4845)]New thread started[Switching to Thread 0xb7fecb70 (LWP 4845)]Breakpoint 2, thread_start (arg=0x0) at testthread.cpp:2929 pthread_cleanup_push(cleanup_handler, NULL);(gdb) n31 curr = start = time(NULL);(gdb) n33 while (!done) {(gdb) nBreakpoint 3, thread_start (arg=0x0) at testthread.cpp:3434 pthread_testcancel(); /* A cancellation point */(gdb) nCanceling thread[Switching to Thread 0xb7fee6d0 (LWP 4840)]Breakpoint 7, main (argc=1, argv=0xbffff374) at testthread.cpp:7070 s = pthread_cancel(thr);(gdb) ncnt = 0[Switching to Thread 0xb7fecb70 (LWP 4845)]Breakpoint 4, thread_start (arg=0x0) at testthread.cpp:3939 cnt++;(gdb) n33 while (!done) {(gdb) nBreakpoint 3, thread_start (arg=0x0) at testthread.cpp:3434 pthread_testcancel(); /* A cancellation point */(gdb) n35 if (curr < time(NULL)) (gdb) n37 curr = time(NULL);(gdb) n38 printf("cnt = %dn", cnt); /* A cancellation point */(gdb) ncnt = 1[Switching to Thread 0xb7fee6d0 (LWP 4840)]Breakpoint 8, main (argc=1, argv=0xbffff374) at testthread.cpp:7575 s = pthread_join(thr, &res);(gdb) n[Switching to Thread 0xb7fecb70 (LWP 4845)]Breakpoint 4, thread_start (arg=0x0) at testthread.cpp:3939 cnt++;(gdb) n33 while (!done) {(gdb) nBreakpoint 3, thread_start (arg=0x0) at testthread.cpp:3434 pthread_testcancel(); /* A cancellation point */(gdb) nBreakpoint 1, cleanup_handler (arg=0x0) at testthread.cpp:1818 printf("Called clean-up handlern");(gdb) nCalled clean-up handler19 cnt = 0;(gdb) n20 }(gdb) n
参考:
【1】http://blog.csdn.net/yanook/article/details/6589798
【2】http://blog.csdn.net/yulanarti/article/details/6197769
【3】http://www.kernel.org/doc/man-pages/online/pages/man3/pthread_cleanup_push.3.html
原文链接: https://www.cnblogs.com/mydomain/archive/2011/08/15/2139850.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/30559
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!