《unix高级环境编程》线程——线程终止

pthread_exit 函数和 pthread_join 函数

        在进程中,若调用了函数 exit_exit,或_Exit 时,则该进程会终止,同样,若进程中的线程调用这三个函数时也会使线程所在的进程终止。那么要是只是退出线程,而不终止线程所在的进程有什么办法?下面是在单线程模式下退出线程的三种方式(不会终止线程所在的进程):

  1. 线程只是从启动例程中返回,返回值是线程的退出码;
  2. 线程被同一进程的其他线程取消;
  3. 线程调用 pthread_exit 函数;
/* 线程终止 */

/*
 * 函数功能:退出线程;
 * 无返回值;
 * 函数原型:
 */
#include <pthread.h>
void pthread_exit(void *rval_ptr);
/*
 * 说明:
 * rval_ptr是一个无类型指针,与传给启动例程的单个参数类似;
 * 进程中的其他线程可以通过调用pthread_join函数访问到这个指针;
 */

/*
 * 函数功能:获取其他已终止的线程的退出码;
 * 返回值:若成功则返回0,否则返回错误编码;
 * 函数原型:
 */
int pthread_join(pthread_t thread, void **rval_ptr);
/*
 * 说明:
 * 调用pthread_join的线程将一直阻塞,直到thread指定的线程调用pthread_exit、从启动例程返回或被取消;
 * 如果线程只是从启动例程返回,rval_ptr将包含返回码;
 * 如果线程是被取消,由rval_ptr指向的内存单元设置为PTHREAD_CANCELED;
 */
        通常父进程需要调用 wait 函数族等待子进程,避免子进程成为僵尸进程。在线程中为确保终止线程的资源对进程可用,即回收终止线程的资源,应该在每个线程结束时分离它们。一个没有被分离的线程终止时会保留其虚拟内存,包括它们的堆栈和其他系统资源。分离线程意味着通知系统不再需要此线程,允许系统将分配给它的资源回收。

        调用 pthread_join 函数将自动分离指定的线程,被分离的线程就再也不能被其他线程连接了,即恢复了系统资源。若线程已处于分离状态,调用 pthread_join 会失败,将返回 EINVAL。所以,如果多个线程需要知道某个特定的线程何时结束,则这些线程应该等待某个条件变量而不是调用 pthread_join
       下面针对这两个函数进行测试:
#include "apue.h"
#include <pthread.h>

static void *func1(void *arg);
static void *func2(void *arg);

int main(void)
{
    pthread_t tid1, tid2;
    int err;
    void *tret;

    err = pthread_create(&tid1, NULL, func1, NULL);
    if(err != 0)
        err_quit("can't create thread 1: %s\n", strerror(err));
    err = pthread_create(&tid2, NULL, func2, NULL);
    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);
}

static void *func1(void *arg)
{
    printf("thread 1 returning\n");
    return((void *)1);
}

static void *func2(void *arg)
{
    printf("thread 2 exiting\n");
    pthread_exit((void*)2);
}

输出结果:
thread 1 returning
thread 2 exiting
thread 1 exit code 1
thread 2 exit code 2

pthread_cancel 函数

          在同一进程中,线程可以请求取消同一进程的其他线程, pthread_cancel 函数可以实现该功能。
/*
 * 函数功能:请求取消同一进程的其他线程;
 * 返回值:若成功则返回0,否则返回错误;
 * 函数原型:
 */
int pthread_cancel(pthread_t tid);
/*
 * 说明:
 * 在默认情况下,该函数会使得由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数,
 * 但是线程可以选择忽略取消方式或控制取消方式;
 * 注意:pthread_cancel并不等待线程终止,它仅仅提出请求;
 */

 pthread_cleanup_push 函数和 pthread_cleanup_pop 函数

       在进程中可以调用 atexit 函数注册在 main 结束后调用清理程序的函数。同样,线程也可以安排它退出时需要调用的清理处理程序函数。可以把每个线程考虑为有一个活动的清理处理程序函数的栈。调用 pthread_cleanup_push 将清理处理程序函数加到栈中,调用 pthread_cleanup_pop 删除最近添加的清理处理程序函数。当线程退出时,从最近添加的清理处理程序函数开始,各个活动的清理处理程序函数都将被调用,当所有清理处理程序函数返回时,线程被终止。当 pthread_cleanup_pop 中的参数 execute 为非 0 时,最近添加的清理处理程序函数也将被调用,然后删除。
/*
 * 函数功能:线程清理处理程序;
 * 无返回值;
 * 函数原型:
 */
void pthread_cleanup_push(void(*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);
/*
 * 说明:
 * 当线程执行以下动作时调用清理函数,调用参数为arg:
 * (1)调用pthread_exit函数时;
 * (2)响应取消请求时;
 * (3)用非零execute参数调用pthread_cleanup_pop时;
 * 如果参数execute设置为0,清理程序函数不被调用;
 */
测试程序:
#include <pthread.h>
#include <sys/types.h>
#include "apue.h"

static void cleanup(void *arg);  //线程清理函数
void *func1(void *arg);
void *func2(void *arg);

int main(void)
{
    pthread_t tid1;
    pthread_t tid2;
    int       err;
    void   *tret;

    err = pthread_create(&tid1, NULL, func1, (void*)1);
    if(err != 0)
        err_quit("can't create thread 1: %s\n", strerror(err));
    err = pthread_create(&tid2, NULL, func2, (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);
}

static void cleanup(void *arg)
{
    printf("cleanup: %s\n", (char*)arg);
}
void *func1(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 *func2(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);
}
输出结果:
thread 1 start.
thread 2 start.
thread 2 push complete.
cleanup: thread 2 second handler
cleanup: thread 2 first handler
thread 1 push complete.
thread 1 exit code 1
thread 2 exit code 2

pthread_detach 函数

        在默认情况下,线程终止状态会保存到对该线程调用 pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被收回。当线程被分离时,并不能用 pthread_join 函数等待它的终止状态,对分离状态的线程进行 pthread_join 的调用会产生失败,返回 EINVAL。pthread_detach 调用可以用于线程进入分离状态。如果不想等待创建的某个线程,而且知道不再需要控制它,就可以调用 pthread_detach 来分离它。线程可以分离自己,任何知道其ID的其他线程也可以随时分离它。
/*
 * 函数功能:使线程处于分离状态;
 * 返回值:若成功则返回0,否则返回错误编码;
 * 函数原型:
 */
int pthread_detach(pthread_t tid);
测试程序:
#include <pthread.h>
#include "apue.h"
void *thr_fn(void *arg)
{
        printf("thread start ...\n");
        printf("thread exiting...\n");
        pthread_exit((void *)1);
}

int main(void)
{
        pthread_t tid1;
        int err;
        void* tret;

        err = pthread_create(&tid1, NULL, thr_fn, (void*)1);
        if(err != 0)
            err_quit("pthread_create error: %s\n", strerror(err));

        err = pthread_detach(tid1);
        if(err != 0)
            err_quit("pthread_detach error: %s\n", strerror(err));

        err = pthread_join(tid1,&tret);
        if(err != 0)
            err_quit("pthread_join error: %s\n", strerror(err));
        printf("thread 1 exit code:%d\n",(int)tret);
        exit(0);
}
输出结果:
thread start ...
thread exiting...
pthread_join error: Invalid argument
因为已经使用 pthread_detach 对线程进行分离,第二次调用 pthread_join 对线程进行分离时会返回 EINVAL。

参考资料:
《UNIX高级环境编程》

你可能感兴趣的:(函数,函数,函数,函数,pthread_detach,pthread_cancel,pthread_exit,pthread_join)