【Linux】线程的终止

线程的终止分为两种情况,一是随着它所属进程的终止而终止,二是仅有单个线程自己终止。
对于第一种进程终止的情况:

  • 如果进程中的任意线程调用了exit、_Exit、或_exit,那么整个进程就会终止,当然属于这个进程的所有线程也会相应终止。
  • 如果线程收到一个信号,这个信号的默认动作是终止进程,那么整个进程会被终止。

对于第二种单个线程终止的情况,线程可以通过以下三种方式退出,而不会终止整个进程:

  • 线程可以简单地从启动例程中返回,返回值是线程的退出码。(使用return)
  • 线程可以被同一进程中的其他线程取消。
  • 线程调用pthread_exit

pthread_exit函数

原型:
    #include <pthread.h>
    void pthread_exit(void *rval_ptr);

调用该函数可以使线程终止,并返回一个指针。rval_ptr参数是一个无类型指针,在线程终止时可以向其中写入要返回的数据,类似于函数pthread_create中的参数指针,我们可以通过它传递一个包含复杂信息的结构的地址,不过要保证这个结构的内存在线程终止后仍然是有效的,进程中的其他线程可以通过pthread_join函数访问到这个指针。

pthread_join函数

原型:
    #include <pthread.h>
    int pthread_join(pthread_t thread, void **rval_ptr);
                            返回值:若成功,返回0 否则,返回错误编号

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函数

原型:
    #include <pthread.h>
    int pthread_cancel(pthread_t tid);
            返回值:若成功,返回0 否则,返回错误编号

线程可以通过调用pthread_cancel函数来请求取消同一进程中的其它线程。在默认情况下,pthread_cancel函数会使由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数。但是,线程可以选择忽略取消或者控制如何被取消。
注意,pthread_cancel并不等待线程终止,它只是提出请求。

pthread_cleanup_push 和 pthread_cleanup_pop 函数

原型:
    #include <pthread.h>
    void pthread_cleanup_puah(void (*rtn)(void*), void *arg);
    void pthread_cleanup_pop(int execute);

线程可以安排它退出时需要调用的函数,这与进程可以通过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 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值