一、线程退出的 3 种方式

方式触发者本质资源回收
线程执行完函数返回线程自己隐式退出,返回值就是 return 的值必须 joindetach
pthread_exit()线程自己主动退出,可指定返回值必须 joindetach
pthread_cancel()其他线程请求取消(异步/延迟)必须 joindetach

⚠️ 无论哪种方式,线程结束后其资源(栈、TLS、内核结构)不会自动完全释放,除非被 joindetach

场景推荐做法
需要线程结果pthread_join()
不需要结果, fire-and-forgetpthread_detach()(创建后立即 detach)
需要中途终止线程pthread_cancel() + pthread_join(),但尽量用条件变量/标志位优雅退出
避免僵尸线程确保每个线程都被 joindetach

1. pthread_exit(void *retval)

关键点:

  • 只能由线程自己调用(你笔记里写了,这是对的)
  • retval 不能指向线程栈上的局部变量(线程退出后栈销毁,指针悬空)
  • 主线程调用 pthread_exit() 不会导致进程退出,只会导致主线程退出,其他线程继续运行
void *my_thread(void *arg)
{
    printf("线程执行---\n");
    int *result = malloc(sizeof(int));
    *result = 42;
    
    pthread_exit(result);  // 主动退出,返回 42
    // 或者直接用 return result; 效果一样
}

int main(void)
{
    pthread_t pthread;
    pthread_create(&pthread, NULL, my_thread, NULL);
    while (1)
    {
        /* code */
        sleep(1);
        printf("主线程---\n");
    }
    return 0;
}

2.pthread_join(pthread_t thread, void **retval)

作用:

  • 阻塞等待指定线程结束
  • 回收线程资源(栈、内核结构)
  • 获取线程返回值(通过 retval 输出参数)

⚠️ 坑:

  • 一个线程只能被 join 一次
  • 如果线程已经被 detach,再 join 会返回 EINVAL
  • join 也不 detach → 线程变成"僵尸线程",资源泄漏
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *my_thread(void *arg)
{
    sleep(3);
    printf("线程停止---\n");
}

int main(void)
{
    pthread_t pthread;
    pthread_create(&pthread,NULL,my_thread,NULL);
    void *result;
    pthread_join(pthread,result);
    printf("主线程\n");
    return 0;
}

3. pthread_detach(pthread_t thread)

作用:

  • 告诉系统:我不关心这个线程的返回值,它结束后自动回收资源
  • detach 后线程变成"分离状态",结束后立即释放资源,不可 join

适用场景:

  • 不需要知道线程结果(如后台守护线程、日志写入线程)
  • 避免忘记 join 导致资源泄漏

⚠️ 坑:

  • detach 后不能再 join
  • 如果线程返回了堆内存指针,detach 后没人收,会内存泄漏
void *my_thread(void *arg)
{
    printf("子线程运行---\n");
    return NULL;
}
int main(void)
{
    pthread_t pthread;
    pthread_create(&pthread,NULL,my_thread,NULL);
    printf("子线程开始运行---\n");
    pthread_detach(pthread);
    printf("不阻塞,继续执行主线程\n");
    sleep(1);
    printf("主线程结束--\n");
    return 0;
}

4. pthread_cancel(pthread_t thread)

关键点:

  • 只是"请求"取消,不是强制杀死
  • 目标线程必须开启取消机制才会响应(默认是开启的)
  • 取消是异步的,目标线程可能在**取消点(cancellation point)**才实际退出

⚠️ 大坑:

  • 异步取消可能导致资源泄漏(锁没释放、内存没 free)
  • 延迟取消更安全,但线程可能卡在非取消点的死循环里,永远退不出
  • 取消后必须 join 来回收资源!
void *my_thread(void *arg)
{
    while (1)
    {
        printf("子线程运行----\n");
        sleep(1);
    }
    return NULL;
}
int main(void)
{
    pthread_t pthread;
    pthread_create(&pthread,NULL,my_thread,NULL);
    printf("3s后请求取消子线程\n");
    sleep(3);
    pthread_cancel(pthread);
    printf("子线程已取消\n");
    sleep(3);
    printf("主线程终止\n");
    return 0;
}