Linux学习日记18:线程的分离

一、前言

前面我们学习了线程的一些基础知识,学习了线程的创建与使用,今天我们来学习线程的分离与同步。

二、线程分离

2.1、函数原型

函数原型如下:

cs 复制代码
#include <pthread.h>
int pthread_detach(pthread_t thread);

参数:thread:设置为分离态的线程 ID(用户态 TID,由 pthread_create() 返回的 ID)。

返回值:成功:返回0;失败:返回对应的错误码。

2.2、工作原理

pthread_detach()本质是**修改线程的分离状态属性,**通知内核:该线程终止时,无需保留资源等待pthread_join(),自动回收资源;具体执行流程如下:

1、**用户态方面:**pthread库修改该线程对应的属性结构体(pthread_attr_t)中detachstate字段为PTHREAD_CREATE_DETACHED;

2、内核态层面: 库通过系统调用通知内核,标记该线程的 task_struct(任务结构体)为 "分离态";

3、**线程终止时:**内核检测到分离态标记,会立即清理线程的栈、寄存器、TID 等资源,无需主线程干预。

注:分离操作不影响线程的执行 ,仅改变线程终止后的资源回收规则。

2.3、典型示例

1、先创建再detach

先创建一个pthread_detach.c文件,然后输入以下代码:

cs 复制代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

void *myfun(void *arg)
{
        printf("child pthread id is %ld\n",pthread_self());
        for(int i=0;i<5;i++)
        {
                printf("children i =%d\n",i);
                sleep(1);
        }
        return NULL;//子线程结束,自动回收资源并退出
}
int main()
{
        pthread_t pthid;
        int ret;
        ret = pthread_create(&pthid,NULL,myfun,NULL);//线程创建
        pthread_detach(pthid);//线程分离
        if(ret != 0)
        {
                printf("error number is %d\n",ret);
                printf("%s\n",strerror(ret));
        }
        printf("parent pthread id is %ld\n",pthread_self());
        for(int i=0;i<5;i++)
        {
                printf("parent i = %d\n",i);
                sleep(1);
        }
        sleep(2);//休眠2s,防止主线程快速退出导致进程退出
        return 0;
}

使用gcc编译器进行编译,运行结果如下:

可以看到,主线程并不会等待子进程,子进程在后台跑,子线程自动释放资源,这就是线程分离的目的。

2、创建时直接设置为分离态

有时不想手动设置分离态,可以让线程创建时直接分离,具体代码如下:

cs 复制代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

void *myfun(void *arg)
{
        printf("child pthread id is %ld\n",pthread_self());
        for(int i=0;i<5;i++)
        {
                printf("children i =%d\n",i);
                sleep(1);
        }
        return 0;
}
int main()
{
        pthread_t pthid;
        pthread_attr_t attr;
        //init
        pthread_attr_init(&attr);//初始化线程属性对象
        //set
        pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置线程为分离态
        //set2
        int ret;
        ret = pthread_create(&pthid,&attr,myfun,NULL);//创建线程,并使其天生就是分离状态
        pthread_detach(pthid);
        if(ret != 0)
        {
                printf("error number is %d\n",ret);
                printf("%s\n",strerror(ret));
        }
        printf("parent pthread id is %ld\n",pthread_self());
        for(int i=0;i<5;i++)
        {
                printf("parent i = %d\n",i);
                sleep(1);
        }
        sleep(2);
        //kill attr
        pthread_attr_destory(&attr);//销毁属性对象(不影响已经创建的线程)
        return 0;
}

使用gcc编译器进行编译,运行结果如下:

运行结果与第一种方法一样,但是这是创建时直接分离,避免了线程创建失败而导致后续分离操作无效的情况。

2.4、注意事项

1、分离状态不可逆: 线程一旦通过 pthread_detach() 设为分离态,无法再改回 joinable状态。如果后续试图调用 pthread_join(),会直接返回 EINVAL 错误。

2、调用时机越早越好,避免线程提前终止:若主线程创建子线程后,还没来得及调用 pthread_detach(),子线程就已经终止,此时调用 pthread_detach() 会返回 ESRCH(找不到线程);

3、不能对已终止的线程调用pthread_detach():线程终止后,其 TID 可能被内核复用(分配给新创建的线程),此时调用 pthread_detach() 可能误操作新线程,导致不可预期的问题。

4、分离线程崩溃仍会导致进程终止:pthread_detach() 仅改变资源回收方式,不改变线程与进程的资源共享关系:如果分离线程触发段错误、除零等异常,仍会发送信号终止整个进程。

5、主线程提前退出仍会杀死分离线程:分离线程只是 "终止后自动回收资源",但如果主线程直接return/exit导致进程终止,所有分离线程仍会被强制杀死(需主线程 pthread_exit()或延时等待)。

6、不能同时调用 pthread_join() 和 pthread_detach():若先调用 pthread_join()阻塞等待线程,此时再调用 pthread_detach() 会返回 EINVAL;若先调用 pthread_detach() 分离线程,再调用 pthread_join()也会返回 EINVAL;

7、主线程无需分离(无意义):主线程的 TID 等于进程 PID,分离主线程不会改变 "主线程退出导致进程终止" 的规则,因此对主线程调用 pthread_detach()无实际意义,还可能返回 EINVAL 错误。

2.5、两种创建方法对比

二者对比如下:

特性 pthread_detach()(动态分离) pthread_attr_setdetachstate()(静态创建)
调用时机 线程创建后任意时间(需线程未终止) 线程创建前(初始化属性时)
灵活性 高(运行时决定是否分离) 低(创建前确定,无法动态调整)
代码复杂度 低(无需初始化属性结构体) 高(需初始化 / 销毁属性结构体)
风险 可能因线程提前终止导致调用失败 无此风险(创建时直接设为分离态)
适用场景 运行时才确定是否分离的场景 提前确定无需等待的后台线程
相关推荐
程序猿乐锅4 分钟前
【从0-1学习Python | 第四篇】Python 字符串和元组
java·开发语言·笔记·后端·python·学习
专注API从业者4 分钟前
告别手工盯盘:用 Open‑Claw 搭建电商商品自动化监控与数据分析实战
运维·数据结构·数据挖掘·数据分析·自动化
Rebecca_aLi5 分钟前
码匠教育:从手工表格到脚本自动化,Python 改造工作流的思路
运维·python·自动化
Yan卌8 分钟前
探析全球虫害治理前沿 —— 美国 PestWorld 展会简述
大数据·运维·人工智能
小此方8 分钟前
Linux网络(二十二):深入解析IP协议:从地址寻址、头部结构到内核机制与TTL环路控制
linux·网络·tcp/ip
Ruiery9 分钟前
Linux 6.6内核内存管理深度解析(四):大页(HugeTLB / THP)— 为什么一个页表项要管 2MB
linux·运维·服务器
Lsetea13 分钟前
OpenSSL报certificate rejected:error 28与CA辅助信任属性排查
linux·https·ssl证书·openssl·证书链
沫璃染墨15 分钟前
从零入门计算机网络系列(五):UDP套接字编程实战——从核心API到客户端与服务器通信
linux·网络·c++·vscode·网络协议·udp
li星野17 分钟前
【学习记录】DAP调试器电路原理图完全详解:从STM32最小系统到SWD调试接口
stm32·嵌入式硬件·学习
Margrop1 Margrop17 分钟前
别再挨个试清华、中科大、阿里源了!国家队下场的“CERNET 镜像聚合站”,凭什么让你的下载直接起飞?
linux·开源镜像·cernet