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 线程退出
线程可以通过以下四种方式终止:
-
pthread_exit(void *retval);:主动退出。retval是退出状态码,可被其他线程通过pthread_join获取。 -
return:从线程执行函数start_routine中return,效果等同于pthread_exit。 -
pthread_cancel(pthread_t thread);:被同一进程中的其他线程取消。 -
进程终止 :进程中任何线程调用了
exit(),或者主线程 (main函数) 执行结束,都会导致整个进程终止,所有线程随之消亡。
特别注意:
- 如果在主线程 (
main) 中调用pthread_exit(),只会结束主线程,而进程会等待其他所有子线程结束后才销毁。
4.3 线程回收:pthread_join
int pthread_join(pthread_t thread, void **retval);
-
功能:阻塞等待指定的线程结束,并回收其资源,同时获取其退出状态。
-
参数:
-
thread:要等待的线程ID。 -
retval:二级指针,用于接收被等待线程的退出状态值 (pthread_exit或return的值)。
-
-
返回值 :成功返回
0,失败返回错误码。
5. 线程属性 (Thread Attributes)
默认创建的线程是 "可结合 (Joinable)" 属性,其资源必须由其他线程使用 pthread_join 显式回收,否则会造成资源泄漏。
如果确定一个线程不需要被 join,可以将其设为 "分离 (Detached)" 属性。当分离线程结束时,其资源会被系统自动回收。
设置分离属性的两种方法:
-
创建时设置 (使用属性对象):
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); // 销毁属性对象 -
创建后设置 (使用
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:注册一个清理函数routine,arg是其参数。可以多次调用注册多个,执行顺序为后进先出 (LIFO)。 -
pthread_cleanup_pop:弹出并执行最近注册的清理函数。-
execute为非0:执行清理函数。 -
execute为0:仅弹出,不执行。
-
清理函数的触发时机:
-
调用
pthread_cleanup_pop(非0)时。 -
线程被
pthread_cancel()取消时。 -
线程调用
pthread_exit()时(即使紧接着pthread_cleanup_pop(0)也会触发)。
注意 :
pthread_cleanup_push和pthread_cleanup_pop必须以宏方式成对出现,语法上必须在同一个代码块中配对使用。
7. 常用命令
bash
# 查看所有线程(轻量级进程)信息
ps -eLf
# 自定义格式查看关键列:PID, PPID, LWP (线程ID), 状态, 命令
ps -eLo pid,ppid,lwp,stat,comm