当我们要退出线程的时候,如果进程中的任意一个线程调用了exit,_Exit,_exit,那么整个进程就会终止,所以这些函数一般不能在线程中使用.
普通的单个线程有一下3中方式退出,这样不会终止进程
(1)从启动例程中返回,返回值是线程的退出码
(2)线程可以被同一进程中的其他线程取消
(3)线程调用pthread_exit(void *rval)函数,rval是退出码
return 和 pthread_exit的区别:前者是函数的返回,后者是线程的返回.
取消线程:
int pthread_cancel(pthread_t tid)
取消tid指定的线程,成功返回0。但是取消只是发送一个请求,并不意味着等待线程终止,而且发送成功也不意味着tid一定会终止.
取消状态,就是线程对取消信号的处理方式,忽略或者响应。线程创建时默认响应取消信号
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则存入运来的取消动作类型值.
向线程发送信号:
int pthread_kill(pthread_t thread, int sig);
别被名字吓到,pthread_kill可不是kill,而是向线程发送signal。还记得signal吗,大部分signal的默认动作是终止进程的运行,所以,我们才要用sigaction()去抓信号并加上处理函数。
向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。
如果要获得正确的行为,就需要在线程内实现sigaction了。
所以,如果int sig的参数不是0,那一定要清楚到底要干什么,而且一定要实现线程的信号处理函数,否则,就会影响整个进程。
如果int sig是0呢,这是一个保留信号,其实并没有发送信号,作用是用来判断线程是不是还活着。
进程信号处理:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
给信号signum设置一个处理函数,处理函数在sigaction中指定
act.sa_mask 信号屏蔽字
act.sa_handler 信号集处理程序
int sigemptyset(sigset_t *set); 清空信号集
int sigfillset(sigset_t *set); 将所有信号加入信号集
int sigaddset(sigset_t *set, int signum); 增加一个信号到信号集
int sigdelset(sigset_t *set, int signum); 删除一个信号到信号集
多线程信号屏蔽处理
/* int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)*/
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
how = SIG_BLOCK:向当前的信号掩码中添加set,其中set表示要阻塞的信号组。
SIG_UNBLOCK:向当前的信号掩码中删除set,其中set表示要取消阻塞的信号组。
SIG_SETMASK:将当前的信号掩码替换为set,其中set表示新的信号掩码。
在多线程中,新线程的当前信号掩码会继承创造它的线程的信号掩码
一般情况下,被阻塞的信号将不能中断此线程的执行,除非此信号的产生是因为程序运行出错如 SIGSEGV;另外不能被忽略处理的信号 SIGKILL 和 SIGSTOP 也无法被阻塞。
清除操作:
线程可以安排它退出时的清理操作,这与进程的可以用atexit函数安排进程退出时需要调用的函数类似。这样的函数称为线程清理处理程序。线程可以建立多个清理处理程序,处理程序记录在栈中,所以这些处理程序执行的顺序与他们注册的顺序相反
pthread_cleanup_push(void (*rtn)(void*), void *args)//注册处理程序
pthread_cleanup_pop(int excute)//清除处理程序
当执行以下操作时调用清理函数,清理函数的参数由args传入
1、调用pthread_exit
2、响应取消请求
3、用非零参数调用pthread_cleanup_pop