1、简介

介绍一下 pthread_exit 和 pthread_join 这两个函数的用法。并且通过一个练习来完成主线程对子线程的回收。

2、pthread_exit

在线程中禁止调用exit函数,否则会导致整个进程退出,取而代之的是调用pthread_exit函数,这个函数是使一个线程退出,如果主线程调用pthread_exit函数也不会使整个进程退出,不影响其他线程的执行。

  • 函数描述:退出单个进程;
  • 函数原型:void pthread_exit(void *retval);
  • 函数参数:retval表示线程退出状态,通常传NULL。(表示我们不关心线程的退出状态)。

注意
pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了,栈空间就会被回收。

3、pthread_join

  • 函数描述阻塞等待线程退出,获取线程退出状态。其作用,对应进程中的waitpid() 函数。
  • 函数原型:int pthread_join(pthread_t thread, void **retval);
  • 函数返回值:(1)成功:0; (2)失败:错误号;
  • 函数参数:(1)thread:线程ID; (2)retval:存储线程结束状态,整个指针和pthread_exit的参数是同一块内存地址。

4、练习

要求:主线程通过 pthread_join 完成对子线程的回收。并获取子线程的退出状态。

//线程退出函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>

int g_var = 9;
//子线程的处理代码
void* working(void *arg)
{   
    printf("我是子线程,线程ID是%ld\n",pthread_self()); 
    printf("%p\n",&g_var);
    pthread_exit(&g_var); //把线程的退出状态给到g_var这块地址
}
int main()
{
    //创建一个子线程
    pthread_t tid;
    //int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
    //               void *(*start_routine) (void *), void *arg);
    int ret = pthread_create(&tid,NULL,working,NULL);
    if (ret != 0)
    {
        printf("pthread create error!");
        return -1;
    }
    
    printf("子线程创建成功,线程ID:%ld\n",tid);

    printf("我是主线程,线程ID:%ld\n",pthread_self());

    //回收子线程,阻塞等待线程退出,获取线程退出状态
    void *p;
    pthread_join(tid,&p); //第二个是获取线程退出状态,然后给到p,其实p所指的内存就是pthread_exit中的那个参数内存
    int n = *(int *)p;
    printf("子线程退出状态为:%d,[%p]\n", n, p);
    return 0;
}

结果: alt

注意:

  • 如果我们要使用pthread_join,那么pthread_exit的参数就不可以是NULL了,因为 pthread_join 的第二个参数要存储线程的结束状态,此时我们关心线程的结束状态是什么,自然pthread_exit的参数就不可以是NULL了。
  • pthread_exit的参数最好不要是一个栈空间的地址;

线程退出后,状态给到 g_var 内存,然后 pthread_join(tid,&p) 的第二个参数指向了 g_var 这块内存,所以打印p的值的话是g_var的地址,对其取值的话是g_var存放的值。