前言承接上一篇 System V IPC 三大进程间通信机制多进程模型实现了任务并发但进程间切换开销大、通信成本高在高频并发场景下并非最优解。本篇引入更轻量的并发执行单元 —— 线程讲解 Linux 线程的底层本质、POSIX 线程库的创建与回收、线程与进程的资源差异是理解多线程并发、后续线程同步机制的前置基础也是笔试面试的核心必考内容。一、线程核心概念1. 什么是线程进程是操作系统分配资源的基本单位而线程是 CPU 调度和执行的基本单位。一个进程内可以包含一个或多个线程所有线程共享所属进程的地址空间与系统资源仅保留自身独立的执行上下文与栈空间。Linux 系统中没有专门的 “线程” 内核结构体线程本质是轻量级进程LWPLight Weight Process内核同样用task_struct描述和进程共用同一套调度框架核心区别在于是否共享地址空间与系统资源。2. 进程与线程核心对比对比维度进程线程本质定位资源分配的基本单位CPU 调度的基本单位地址空间拥有独立完整的虚拟地址空间无独立地址空间共享所属进程的地址空间切换开销大需切换页表、刷新 TLB、替换进程资源小仅切换寄存器、栈等私有上下文通信方式需要管道、共享内存等 IPC 机制直接通过全局变量、堆内存交换数据稳定性进程间相互独立一个崩溃不影响其他一个线程异常崩溃整个进程终止创建销毁速度慢资源占用高速度快资源占用极低3. 多线程的优劣势优势线程上下文切换开销远低于进程高并发场景下 CPU 利用率更高同进程内线程通信成本极低无需内核中转直接读写共享内存创建、销毁速度快适合短任务高频并发的场景劣势稳定性弱单个线程触发内存错误会导致整个进程崩溃多线程并发访问共享资源需要同步处理增加编程复杂度调试与问题排查难度远高于单进程程序二、POSIX 线程库pthread 基础Linux 标准线程库为 pthreadPOSIX Thread属于用户态库底层封装了内核 clone 系统调用编译时需要链接-lpthread库。1. 线程创建pthread_create#include pthread.h int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);thread传出参数保存创建成功的线程 ID进程内唯一attr线程属性结构体传NULL使用默认属性start_routine线程入口函数格式为void* 函数名(void*)arg传递给线程入口函数的参数返回值成功返回0失败直接返回错误码不设置 errno注意pthread_t是库层面的线程 ID和内核中的 LWP 号不是同一个概念仅在当前进程内有效。2. 线程退出pthread_exitvoid pthread_exit(void *retval);功能主动终止当前线程retval为退出状态可被其他线程通过pthread_join获取与return的区别return只是函数返回仅在线程主入口函数中 return 才会退出线程pthread_exit在任意函数层级调用都会直接终止当前线程main 函数中 return 会终止整个进程所有线程一同退出主线程调用pthread_exit只会退出主线程子线程继续运行核心坑点不能返回线程栈上局部变量的地址线程退出后栈空间会被回收成为野指针。返回值必须使用全局变量或堆内存。3. 线程回收pthread_joinint pthread_join(pthread_t thread, void **retval);功能阻塞等待指定线程退出回收其资源等价于进程的waitpidretval传出参数接收线程的退出状态每个非分离线程必须被 join 一次否则会残留线程资源造成资源泄漏4. 线程分离pthread_detachint pthread_detach(pthread_t thread);功能将指定线程设置为分离状态线程退出后由系统自动回收资源无需手动 join分离后的线程无法再被 join也无法获取其退出返回值适用于不需要关心返回值的后台常驻线程5. 线程取消pthread_cancelint pthread_cancel(pthread_t thread);功能向指定线程发送取消请求请求其终止执行注意这是请求而非强制终止。默认情况下线程会在取消点如系统调用、IO 操作响应退出线程也可以设置取消状态忽略取消请求。三、线程的共享资源与私有资源面试高频线程共享进程的大部分资源但也保留自身独立的执行上下文明确两者边界是理解多线程行为的核心。资源类别线程间共享线程私有内存空间代码段、全局数据段、堆内存、共享映射区线程栈、线程局部存储TLS系统资源文件描述符表、信号处理函数配置寄存器上下文、程序计数器进程属性用户 ID / 组 ID、工作目录、umask 掩码线程 ID、errno 变量、信号屏蔽字调度相关进程优先级基准线程独立调度优先级关键细节errno是每个线程独立的变量多线程下不会互相干扰但文件描述符完全共享一个线程修改文件偏移量其他所有线程都会受影响。四、实战多线程创建与回收#include stdio.h #include pthread.h #include unistd.h #include stdlib.h // 线程1接收参数计算后返回堆上的结果 void *thread_task1(void *arg) { int num *(int *)arg; printf(线程1启动参数%d线程ID%lu\n, num, pthread_self()); int *result malloc(sizeof(int)); *result num * 2; sleep(1); pthread_exit(result); } // 线程2简单任务执行后退出 void *thread_task2(void *arg) { printf(线程2启动线程ID%lu\n, pthread_self()); sleep(2); printf(线程2执行完毕\n); pthread_exit(NULL); } int main(void) { pthread_t tid1, tid2; int param 10; // 创建两个子线程 int ret pthread_create(tid1, NULL, thread_task1, param); if (ret ! 0) { fprintf(stderr, 线程1创建失败\n); return 1; } ret pthread_create(tid2, NULL, thread_task2, NULL); if (ret ! 0) { fprintf(stderr, 线程2创建失败\n); return 1; } printf(主线程运行等待子线程回收\n); // 回收线程1并获取返回值 void *ret_val; pthread_join(tid1, ret_val); printf(线程1已回收计算结果%d\n, *(int *)ret_val); free(ret_val); // 回收线程2 pthread_join(tid2, NULL); printf(线程2已回收程序结束\n); return 0; }编译命令gcc demo.c -o demo -lpthread五、Linux 线程底层本质轻量级进程Linux 的线程实现属于内核级线程和 Windows 的用户态线程不同底层通过clone系统调用创建和fork同源区别在于 clone 可以指定共享哪些资源创建线程时共享地址空间、文件描述符表、信号处理表等进程资源仅分配独立的栈与寄存器上下文内核调度器对进程和线程一视同仁都作为独立调度单元参与 CPU 调度这种实现的优势是线程由内核调度一个线程阻塞不会影响同进程的其他线程劣势是线程操作都需要系统调用有一定开销。六、面试高频考点与易错坑点1. 经典面试问答Q1进程和线程有什么本质区别答进程是操作系统分配资源的基本单位线程是 CPU 调度的基本单位。进程拥有独立的虚拟地址空间线程没有独立地址空间共享所属进程的地址空间与系统资源。进程切换开销大需要切换页表与进程资源线程切换开销小仅切换私有上下文。 进程间通信需要 IPC 机制线程间可直接通过共享内存通信。Q2为什么线程切换的开销远小于进程答 进程切换需要替换整个地址空间的页表、刷新 TLB 缓存同时切换文件描述符表、信号处理等进程资源 而线程共享进程的地址空间切换时只需要保存和恢复线程的寄存器、栈指针等私有执行上下文不需要切换地址空间因此开销小很多。Q3pthread_join 和 pthread_detach 有什么核心区别答 pthread_join 是阻塞等待指定线程退出手动回收线程资源同时可以获取线程的退出返回值 pthread_detach 是将线程设置为分离状态线程退出后由系统自动回收资源不需要手动 join也无法获取返回值。Q4线程退出时return 和 pthread_exit 有什么区别答 在线程入口主函数中两者效果一致都会终止当前线程。 但 return 只是函数返回在线程调用的子函数中 return 只会返回到上一层不会退出线程pthread_exit 在任意层级调用都会直接终止当前线程。 另外 main 函数中 return 会终止整个进程所有线程一同退出主线程调用 pthread_exit 只会退出主线程子线程继续运行。Q5线程之间哪些资源共享哪些私有答 共享资源代码段、全局数据、堆内存、文件描述符表、信号处理方式、用户 ID 与组 ID、工作目录。 私有资源线程 ID、线程栈、寄存器上下文、errno、信号屏蔽字、线程局部存储。2. 常见易错坑点线程退出返回栈上局部变量的地址线程退出后栈空间回收导致野指针必须使用堆内存或全局变量传递返回值编译 pthread 程序忘记添加-lpthread链接选项出现函数未定义报错非分离线程忘记调用 pthread_join 回收造成线程资源泄漏耗尽进程线程数上限误以为 pthread_self 返回的线程 ID 就是内核 LWP 号两者分属不同层面并不等价多线程并发修改全局变量不加同步保护导致数据竞争、计算结果异常main 函数直接 return 退出误以为子线程还能继续运行实际整个进程会终止所有线程被销毁对已分离的线程重复调用 pthread_join导致调用失败以上就是 Linux 线程基础的全部核心内容线程是高并发编程的基础执行单元但共享资源带来的数据竞争问题必须通过同步机制解决。下一篇我们将讲解线程同步的三大核心工具互斥锁、条件变量与读写锁拆解死锁成因与规避方案。制作不易如果对你有用希望能点赞收藏支持一下。