线程的终止分为两种情况,一是随着它所属进程的终止而终止,二是仅有单个线程自己终止。 对于第一种进程终止的情况:
如果进程中的任意线程调用了exit、_Exit、或_exit,那么整个进程就会终止,当然属于这个进程的所有线程也会相应终止。如果线程收到一个信号,这个信号的默认动作是终止进程,那么整个进程会被终止。对于第二种单个线程终止的情况,线程可以通过以下三种方式退出,而不会终止整个进程:
线程可以简单地从启动例程中返回,返回值是线程的退出码。(使用return)线程可以被同一进程中的其他线程取消。线程调用pthread_exit调用该函数可以使线程终止,并返回一个指针。rval_ptr参数是一个无类型指针,在线程终止时可以向其中写入要返回的数据,类似于函数pthread_create中的参数指针,我们可以通过它传递一个包含复杂信息的结构的地址,不过要保证这个结构的内存在线程终止后仍然是有效的,进程中的其他线程可以通过pthread_join函数访问到这个指针。
pthread_join函数会阻塞调用线程,直到指定的线程 调用pthread_exit、从启动例程中返回、或者被取消。
如果指定的线程调用pthread_exit而终止,二重指针rval_ptr就会指向pthread_exit中的指针 rval_ptr。如果指定的线程通过return直接从启动例程返回,那么rval_ptr会包含返回码。如果指定的线程被其他线程取消,那么rval_ptr所指定的内存单元就被置为PTHREAD_CANCELED。如果对线程的返回值不感兴趣,可以将rval_ptr置为NULL,这样调用pthread_jion函数就可以等待指定线程终止,而不必获取线程的终止状态。默认情况下,线程的终止状态会一直保存直到对该线程调用pthread_join,如果线程已被分离,那么线程的底层存储资源可以在终止时被立即收回,所以在线程被分离后,我们不能对其调用pthread_join。pthread_join可以自动的将线程至于分离状态,如果线程已处于分离状态,调用pthread_join会失败,返回EINVAL。使用函数pthread_detach可以使线程置于分离状态。
线程可以通过调用pthread_cancel函数来请求取消同一进程中的其它线程。在默认情况下,pthread_cancel函数会使由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数。但是,线程可以选择忽略取消或者控制如何被取消。 注意,pthread_cancel并不等待线程终止,它只是提出请求。
线程可以安排它退出时需要调用的函数,这与进程可以通过atexit函数安排退出类似。这样的函数被称为线程清理处理程序。一个线程可以建立多个线程清理处理程序,它们被记录在栈中,所以它们的执行顺序与注册顺序相反。这里的pthread_cleanup_push 和 pthread_cleanup_pop 这两个函数就是用于处理程序的出入栈的。 当线程执行以下动作时,清理函数rtn将会被调用,它的参数由指针arg来传递。
调用pthread_exit响应取消请求时用非零execute参数调用pthread_cleanup_pop时特别注意,使用return从线程返回时,清理处理程序是不会被调用的如果execute参数设置为0,那么清理程序不会被调用,不过无论是上述的哪种情况,pthread_cleanup_pop都会删除上次pthread_cleanup_push所建立的清理处理程序。 在这里还要注意,由于这两个函数可能被实现为宏,所以在使用时应当成对出现。
下面的代码给出一个使用上述这些函数的例子: 我们创建两个线程,在线程1中安排两个线程清理处理程序,在线程2中发送一个取消线程1的请求,并在主线程中使用pthread_join等待线程1的终止,并获取它的终止状态。
#include <stdio.h> #include <pthread.h> void cleanup1() { printf("clean up 1 \n"); } void cleanup2() { printf("clean up 2 \n"); } void *pth_func1(void *args) { pthread_t tid; tid = pthread_self(); pthread_cleanup_push(cleanup1, NULL); pthread_cleanup_push(cleanup2, NULL); printf("thread 0x%lx push complete\n", (unsigned long)tid); if(args) pthread_exit((void *)1); pthread_cleanup_pop(0); pthread_cleanup_pop(0); return (void *)0; } void *pth_func2(void *tid) { pthread_t stid; stid = pthread_self(); printf("in thread 0x%lx : ", (unsigned long)stid); printf("canceled thread 0x%lx \n", (unsigned long)tid); pthread_cancel((pthread_t)tid); return (void *)2; } int main() { pthread_t tid1, tid2; void *tret; pthread_create(&tid1, NULL, pth_func1, (void *)1); pthread_create(&tid2, NULL, pth_func2, (void *)tid1); pthread_join(tid1, &tret); pthread_join(tid2, NULL); printf("thread 0x%lx return with %ld \n", (unsigned long)tid1, (long)tret); }运行结果(Ubuntu 16.04)
thread 0x7fa0f4c2c700 push complete in thread 0x7fa0f442b700 : canceled thread 0x7fa0f4c2c700 clean up 2 clean up 1 thread 0x7fa0f4c2c700 return with 1