线程创建、执行、退出、回收

1. 进程与线程的基本概念
特性 进程 (Process) 线程 (Thread)
别名 重量级进程 轻量级进程 (LWP)
核心作用 资源分配的最小单位 CPU调度的基本单位
侧重点 侧重于拥有独立的资源,是一个资源的"容器" 侧重于执行,是CPU执行的"工人"
组成 PCB (进程控制块) + 完整的地址空间 (代码段、数据段、BSS段、堆、栈) 线程ID + 程序计数器(PC) + 一组寄存器 + 独立的栈空间
关系 进程是线程的容器 线程必须存在于进程之中,共享进程的所有资源
开销 创建、切换开销大 创建、切换开销小 (共享资源,无需切换地址空间)

2. Linux 线程实现模型
  • 历史 :Linux早期内核并不支持真正的线程。通过 clone() 系统调用,可以创建一个与父进程共享地址空间的"轻量级进程"来模拟线程。

  • 现状 (NPTL) :现在Linux使用 NPTL (Native POSIX Thread Library) 作为默认的线程库。它提供了对POSIX标准的完整支持,性能优异。

  • 核心观点 :在Linux内核视角中,不严格区分进程和线程 ,它们都被称为"任务 (Task)",通过 clone() 系统调用创建,只是创建时传递的"共享标志 (flags)"不同。进程共享资源少,线程共享资源多。


3. 线程的基本操作

使用线程库,编译时需要链接 pthread 库:gcc -pthread your_program.c

操作 相关函数 类比进程
1. 创建 pthread_create fork
2. 执行 线程执行函数 start_routine 子进程的代码
3. 退出 pthread_exit, return, pthread_cancel exit, _exit, main函数返回
4. 回收 pthread_join, pthread_detach wait, waitpid

4. 核心API函数详解
4.1 线程创建:pthread_create
复制代码
int pthread_create(pthread_t *thread, 
                   const pthread_attr_t *attr,
                   void *(*start_routine) (void *), 
                   void *arg);
  • 功能:创建一个新线程。

  • 参数

    • thread:传出参数,用于存储新创建线程的ID。

    • attr:线程属性。设置为 NULL 表示使用默认属性(可结合属性)。

    • start_routine:函数指针,新线程创建后将立即执行的函数。

    • arg:传递给 start_routine 的参数。

  • 返回值 :成功返回 0,失败返回错误码。


4.2 线程退出

线程可以通过以下四种方式终止:

  1. pthread_exit(void *retval); :主动退出。retval 是退出状态码,可被其他线程通过 pthread_join 获取。

  2. return :从线程执行函数 start_routinereturn,效果等同于 pthread_exit

  3. pthread_cancel(pthread_t thread);:被同一进程中的其他线程取消。

  4. 进程终止 :进程中任何线程调用了 exit(),或者主线程 (main 函数) 执行结束,都会导致整个进程终止,所有线程随之消亡。

特别注意

  • 如果在主线程 (main) 中调用 pthread_exit()只会结束主线程,而进程会等待其他所有子线程结束后才销毁。

4.3 线程回收:pthread_join
复制代码
int pthread_join(pthread_t thread, void **retval);
  • 功能:阻塞等待指定的线程结束,并回收其资源,同时获取其退出状态。

  • 参数

    • thread:要等待的线程ID。

    • retval:二级指针,用于接收被等待线程的退出状态值 (pthread_exitreturn 的值)。

  • 返回值 :成功返回 0,失败返回错误码。


5. 线程属性 (Thread Attributes)

默认创建的线程是 "可结合 (Joinable)" 属性,其资源必须由其他线程使用 pthread_join 显式回收,否则会造成资源泄漏

如果确定一个线程不需要被 join,可以将其设为 "分离 (Detached)" 属性。当分离线程结束时,其资源会被系统自动回收。

设置分离属性的两种方法

  1. 创建时设置 (使用属性对象):

    复制代码
    pthread_attr_t attr;
    pthread_attr_init(&attr); // 初始化属性对象
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置分离属性
    
    pthread_create(&tid, &attr, thread_func, NULL); // 创建分离线程
    
    pthread_attr_destroy(&attr); // 销毁属性对象
  2. 创建后设置 (使用 pthread_detach):

    复制代码
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL); // 先创建默认的可结合线程
    pthread_detach(tid); // 立即将其设置为分离状态,无法再被join

回收策略总结

  • 短期任务 :用 pthread_join 等待并回收。

  • 可能阻塞/休眠 :等待一段时间后,可考虑用 pthread_cancel 取消并回收。

  • 长期运行的后台任务:设置为分离属性,让其自动回收。


6. 线程清理函数 (Thread Cleanup Handlers)

当线程被取消或异常退出时,可能需要执行一些清理工作(如释放锁、关闭文件)。可以注册清理函数来实现。

复制代码
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);
  • pthread_cleanup_push :注册一个清理函数 routinearg 是其参数。可以多次调用注册多个,执行顺序为后进先出 (LIFO)

  • pthread_cleanup_pop弹出并执行最近注册的清理函数。

    • execute 为非0:执行清理函数。

    • execute 为0:仅弹出,不执行。

清理函数的触发时机

  1. 调用 pthread_cleanup_pop(非0) 时。

  2. 线程被 pthread_cancel() 取消时。

  3. 线程调用 pthread_exit() 时(即使紧接着 pthread_cleanup_pop(0) 也会触发)。

注意pthread_cleanup_pushpthread_cleanup_pop 必须以宏方式成对出现,语法上必须在同一个代码块中配对使用。


7. 常用命令

bash

复制代码
# 查看所有线程(轻量级进程)信息
ps -eLf

# 自定义格式查看关键列:PID, PPID, LWP (线程ID), 状态, 命令
ps -eLo pid,ppid,lwp,stat,comm
相关推荐
云边有个稻草人1 小时前
飞算JavaAI能处理SaaS套餐变更业务吗?
java·springboot·ai编程·java开发·飞算javaai·java代码生成·ai coding模型
gb42152871 小时前
python中Web应用服务器
开发语言·前端·python
萧瑟余晖2 小时前
Java深入解析篇三十六之分布式系统详解
java·开发语言·分布式
KaKa_大王2 小时前
关于秒杀项目的一些理解
java·学习
ZJU_统一阿萨姆3 小时前
【推理优化进阶】调度器的数学内核:排队论、SLO 与在线决策
开发语言·人工智能·语言模型·系统架构·vllm
tomla3 小时前
使用Java VisualVM观察过期对象引用现象
java
M1A13 小时前
Spring Boot YAML 配置读取完全指南:从基础到微服务
java
古法安卓3 小时前
Android-SELinux 策略调试实战:从 AVC 日志到策略修复
android·java·android studio
todoitbo3 小时前
飞算JavaAI的多租户权限隔离实测
java·springboot·ai编程·java开发·飞算javaai·java代码生成