线程终止:
这块还差一个点,就是线程清理处理程序,县城可以建立多个清理处理程序。处理程序记录在栈中,意味着他们的执行顺序和注册顺序是相反的。
#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);
以下动作执行时调用清理函数:
(1)调用pthread_exit时;
(2)响应取消请求的时候;
(3)用非零execute参数调用pthread_cleanup_pop时
调用的参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push()函数来安排的,函数将rtn函数指针压入栈中,在当前的线程退出的时候被调用,可以发现这个函数针对的是当前的线程。如果execute参数置为0,清理函数将不被调用。无论上面三种情况中的哪一种,pthread_cleanup_pop()都将删除上次pthread_cleanup_push()调用建立的清理处理程序。
值得注意的一点是,这些函数有个限制,由于他们可以实现为宏,所以必须在与线程相同的作用域中以匹配对的形式使用,pthread_cleanup_push的宏定义可以包含字符{,在这种情况下对应的匹配字符}就要在pthread_cleanup_pop定义中出现。
给出书上的一个线程处理函数的一个例子:
/*
*Name : list11_4.c
*Author : Lniper
*Date : 2016-02-17
*Aim : Thread cleanup handler.
*/
#include "apue.h"
#include "APUE_ERROR.h"
#include <pthread.h>
void cleanup(void *arg)
{
printf("cleanup: %s\n", (char *)arg);
}
void *thr_fn1(void *arg)
{
printf("thread 1 start \n");
pthread_cleanup_push(cleanup, "thread 1 first handler");
pthread_cleanup_push(cleanup, "thread 1 second handler");
printf("thread 1 push complete\n");
if(arg)
return ((void *)1);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return ((void *)1);
}
void *thr_fn2(void *arg)
{
printf("thread 2 start\n");
pthread_cleanup_push(cleanup, "thread 2 first handler");
pthread_cleanup_push(cleanup, "thread 2 second handler");
printf("thread 2 push complete\n");
if(arg)
pthread_exit((void *)2);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
pthread_exit((void *)2);
}
int main(void)
{
int err;
pthread_t tid1, tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);
if(err != 0)
err_quit("can't create thread 1: %s\n", strerror(err));
err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);
if(err != 0)
err_quit("can't create thread 2: %s\n", strerror(err));
err = pthread_join(tid1, &tret);
if(err != 0)
err_quit("can't join with thread 1: %s\n", strerror(err));
printf("thread 1 exit code %d\n", (int)tret);
err= pthread_join(tid2, &tret);
if(err != 0)
err_quit("can't join with thread 2: %s\n", strerror(err));
printf("thread 2 exit code %d\n", (int)tret);
exit(0);
}
输出的结果是:
我们可以看出两个线程都正常的启动和退出了,但是只有第二个线程调用了pthread_exit()函数,所以线程处理函数进行了处理,如果是从它的启动进程中返回而终止的话,那么它的清理程序就不会被调用,也可以看到对于线程清理函数是按照与他们安装时的相反顺序被调用的。
此外,默认情况下,线程的终止状态回保存到对该线程pthread_join(),如果线程已经处于分离状态,线程的底层存储资源可以在线程终止的时候立刻被收回。当线程被分离时,并不能用pthread_join()函数等待它的终止状态。对分离状态的线程进行pthread_join()的调用可以产生失败,返回EINVAL。pthread_detach调用可以用于使线程进入分离状态。
#include <pthread.h>
int pthread_detach(pthread_t tid);
APUE中给出了线程和进程的原语的比较:
线程原语 | 进程原语 | 描述 |
fork() | pthread_create() | 创建新的控制流 |
exit() | pthread_exit() | 从现有的控制流中退出 |
waitpid() | pthread_join() | 从控制流中得到退出状态 |
atexit() | pthread_cleanup_push() | 注册在退出控制流时调用的函数 |
getpid() | pthread_self() | 获取控制流的ID |
abort() | pthread_cancel() | 请求控制流的非正常退出 |