线程相关知识
线程是进程内的一条独立执行流是操作系统调度 CPU 的最小单位共享进程的地址空间与资源有自己独立的栈、寄存器、程序计数器。一、核心本质拆解1.从属关系进程是资源分配最小单位内存、文件、句柄线程是CPU 调度最小单位寄生在进程里一个进程至少有 1 个主线程。2.资源共享 私有共享代码段、全局变量、堆内存、文件描述符、信号处理方式私有线程栈、寄存器上下文、PC 程序计数器、线程局部变量 TLS3.轻量级的根本原因创建 / 销毁线程不用重新分配虚拟地址空间只配私有栈和 TCB线程控制块开销远小于进程切换线程不用切换页表、刷新 TLB切换成本极低。二、线程的状态线程五大核心状态汇总表注基于 Linux/Java 通用线程模型适配面试核心考点区分核心特征与流转逻辑线程状态核心定义核心特征触发场景下一状态NEW 新建线程对象已创建操作系统内核线程未初始化未进入调度队列未分配CPU资源未执行任何代码无调度资格程序初始化线程对象尚未调用启动方法调用start()/启动线程 → READY 就绪READY 就绪线程资源已准备完成具备运行条件等待CPU时间片调度不占用CPU常驻调度队列随时可被内核选中执行1. 新建线程启动后2. 线程时间片用完被剥夺CPU3. 阻塞线程等待的事件完成获取CPU时间片 → RUNNING 运行RUNNING 运行线程正在CPU上执行业务代码是唯一占用CPU的状态占用CPU资源执行指令、处理逻辑消耗时间片操作系统调度器选中就绪线程分配CPU时间片1. 时间片耗尽 → READY2. 等待锁/IO/sleep → BLOCKED3. 代码执行完毕 → TERMINATEDBLOCKED 阻塞/等待线程主动放弃CPU暂停执行等待外部事件触发不占用、不抢占CPU彻底释放时间片是并发高效的关键1. 等待锁mutex2. 阻塞式IO读写3. 调用sleep/wait4. 等待条件变量等待事件触发/超时唤醒 → READY 就绪TERMINATED 终止线程代码执行完毕或异常终止生命周期结束彻底结束调度等待系统回收线程栈、TCB等资源无法再次启动1. 线程主方法执行完成2. 线程被主动终止3. 运行中抛出致命异常无生命周期终结面试核心总结只有运行态占用CPU就绪、阻塞态均不占用CPU资源线程不会从阻塞态直接进入运行态必须先回到就绪态等待CPU调度阻塞是主动放弃CPU就绪是等待CPU分配二者本质区别是否具备立刻执行的条件三、线程和进程区别对比维度进程 (Process)线程 (Thread)资源独立性拥有独立的内存空间、文件描述符等系统资源共享所属进程的内存空间和资源仅拥有独立的栈、程序计数器等创建 / 切换开销开销大涉及资源分配与回收、页表切换、TLB 刷新开销小仅需保存少量上下文信息无需切换地址空间通信方式需通过进程间通信IPC机制如信号、消息队列、管道、共享内存、Socket 等可直接通过共享内存通信需同步机制避免冲突如互斥锁、条件变量健壮性一个进程崩溃通常不影响其他进程一个线程崩溃可能导致整个进程崩溃调度单位操作系统资源分配的最小单位操作系统 CPU 调度的最小单位上下文切换成本高需切换页表、刷新 TLB、保存完整进程上下文低仅需保存线程栈、寄存器上下文同进程共享地址空间数据共享效率低需通过 IPC 机制涉及内核态切换与数据拷贝高直接读写进程共享内存无额外拷贝开销适用场景- 任务间需要严格隔离如浏览器标签页- 充分利用多核 CPU 处理 CPU 密集型任务如大规模计算- 长时间运行的独立任务如后台备份、视频转码- 运行不稳定或第三方代码避免崩溃影响整体- 任务间需要频繁共享数据如 Web 服务器处理多请求- 轻量级并发追求低开销如 GUI 程序后台加载数据- I/O 密集型任务如网络请求、文件读写可利用等待时间切换- 短期、协作紧密的子任务如数据分片处理后汇总四、多线程相关函数1.创建与终止函数1pthread_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线程的入口函数指针线程创建后将执行这个函数参数arg传递给入口函数的参数无参数时设为NULL返回值成功返回0线程 ID 写入thread失败返回非 0 错误码2pthread_exit 线程退出项目内容所需头文件#include pthread.h函数原型void pthread_exit(void *retval);功能结束当前线程并可返回退出状态值参数retval传递给回收线程的退出状态值不需要则传NULL返回值无示例代码#include stdio.h #include stdlib.h #include pthread.h // 必须包含 pthread 头文件 #include unistd.h // 用于 sleep() // 1. 定义线程入口函数必须符合 void* (*)(void*) 的签名 void *thread_func(void *arg) { // 把主线程传来的参数强转为 int 类型 int thread_num *(int *)arg; free(arg); // 释放主线程动态分配的参数内存避免内存泄漏 printf(子线程 %d 开始运行\n, thread_num); // 模拟耗时任务 sleep(2); // 2. 线程退出并返回状态值这里用线程号作为示例 pthread_exit((void *)(long)thread_num); } int main() { int thread_count 3; pthread_t threads[thread_count]; // 存储线程ID的数组 for (int i 0; i thread_count; i) { // 动态分配内存传递参数避免栈变量被覆盖的经典坑 int *arg malloc(sizeof(int)); *arg i; // 3. 创建线程 int ret pthread_create( threads[i], // 输出线程ID NULL, // 使用默认线程属性 thread_func, // 线程入口函数 arg // 传递给入口函数的参数 ); if (ret ! 0) { perror(pthread_create 失败); exit(EXIT_FAILURE); } } printf(主线程等待所有子线程结束...\n); // 4. 等待所有子线程结束并回收资源 for (int i 0; i thread_count; i) { void *retval; pthread_join(threads[i], retval); // 阻塞等待子线程退出 // 强转回 int打印子线程的退出状态 printf(子线程 %d 已退出返回值%ld\n, i, (long)retval); } printf(所有子线程已结束主线程退出\n); return 0; }2.线程标识函数1pthread_self函数项目内容所需头文件#include pthread.h函数原型pthread_t pthread_self(void);功能获取调用该函数的线程自身的线程 ID参数无返回值总是成功返回调用线程的线程 ID类型为pthread_tpthread_self() 是无参数、无失败情况的函数调用它只会返回当前线程的 ID。它和 pthread_create 中获取的线程 ID 是同一种类型 pthread_t但不能直接和操作系统的 LWP轻量级进程 ID划等号。常用于线程内部打印日志、调试或作为线程的唯一标识使用2示例代码#include stdio.h #include stdlib.h #include pthread.h #include unistd.h // 线程入口函数 void *thread_func(void *arg) { int thread_num *(int *)arg; free(arg); // ✅ 关键用 pthread_self() 获取当前线程的ID pthread_t tid pthread_self(); printf(子线程 %d 运行线程ID: %lu\n, thread_num, (unsigned long)tid); sleep(1); pthread_exit((void *)(long)thread_num); } int main() { int thread_count 2; pthread_t threads[thread_count]; printf(主线程启动主线程ID: %lu\n, (unsigned long)pthread_self()); for (int i 0; i thread_count; i) { int *arg malloc(sizeof(int)); *arg i; int ret pthread_create(threads[i], NULL, thread_func, arg); if (ret ! 0) { perror(pthread_create failed); exit(EXIT_FAILURE); } } // 等待所有子线程结束 for (int i 0; i thread_count; i) { void *retval; pthread_join(threads[i], retval); printf(子线程 %d 已退出\n, (int)(long)retval); } printf(所有子线程结束主线程退出\n); return 0; }1.pthread_t 类型它是一个不透明类型通常是无符号长整型所以打印时需要强转为 unsigned long 类型。2.pthread_self() vs gettid()pthread_self() 返回的是用户态线程库中的线程 ID仅在进程内唯一。gettid() 返回的是内核 LWP ID全局唯一但不是标准库函数需要系统调用。3.典型用途线程内部调试日志打印当前线程 ID线程局部存储TLS中作为键使用线程间互斥 / 同步时标识身份3.线程回收函数1pthread_join 线程回收项目内容所需头文件#include pthread.h原型int pthread_join(pthread_t thread, void **retval);功能阻塞等待指定的结合态线程结束并回收其资源参数thread要回收的线程 ID参数retval接收线程退出状态值的指针不关心则传NULL返回值成功返回0失败返回非 0 错误码2pthread_attr_init 初始化线程属性项目内容所需头文件#include pthread.h原型int pthread_attr_init(pthread_attr_t *attr);功能将一个线程属性对象初始化为默认值参数attr需要初始化的线程属性对象指针返回值成功返回0失败返回非 0 错误码3pthread_attr_setdetachstate 设置分离状态项目内容所需头文件#include pthread.h原型int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);功能设置线程属性对象的分离状态参数attr要设置的线程属性对象指针参数detachstate线程状态-PTHREAD_CREATE_JOINABLE结合态默认需join回收-PTHREAD_CREATE_DETACHED分离态系统自动回收资源返回值成功返回0失败返回非 0 错误码4pthread_attr_destroy 销毁线程属性项目内容所需头文件#include pthread_attr_t *attr);功能销毁一个已初始化的线程属性对象参数attr要销毁的线程属性对象指针返回值成功返回0失败返回非 0 错误码5结合态与分离态状态特点适用场景结合态 (JOINABLE)线程结束后资源不会自动释放需其他线程调用pthread_join回收可获取退出状态主线程需要等待子线程完成、需要子线程返回结果的场景分离态 (DETACHED)线程结束后资源由系统自动回收无需也无法join无法获取退出状态主线程不关心子线程结果只想让其后台运行的场景6示例代码#include stdio.h #include stdlib.h #include pthread.h #include unistd.h // 线程入口函数 void *thread_func(void *arg) { int id *(int *)arg; free(arg); printf(线程 %d 开始运行tid: %lu\n, id, (unsigned long)pthread_self()); sleep(1); // 模拟耗时任务 printf(线程 %d 运行结束\n, id); pthread_exit((void *)(long)id); } int main() { pthread_t tid_joinable, tid_detached; pthread_attr_t attr; // -------------------- 1. 创建结合态线程默认 -------------------- int *arg1 malloc(sizeof(int)); *arg1 1; pthread_create(tid_joinable, NULL, thread_func, arg1); // -------------------- 2. 创建分离态线程通过属性设置 -------------------- int *arg2 malloc(sizeof(int)); *arg2 2; // 初始化线程属性 pthread_attr_init(attr); // 设置为分离态 pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); // 使用自定义属性创建线程 pthread_create(tid_detached, attr, thread_func, arg2); // 销毁属性对象属性对象可以重复使用这里用完直接销毁 pthread_attr_destroy(attr); // -------------------- 3. 回收结合态线程 -------------------- void *retval; printf(主线程等待结合态线程1结束...\n); pthread_join(tid_joinable, retval); printf(结合态线程1已回收退出状态%ld\n, (long)retval); // 注意分离态线程无法被join也不需要手动回收 printf(主线程结束分离态线程2将由系统自动回收资源\n); sleep(2); // 等待分离态线程执行完毕防止主线程提前退出 return 0; }7核心注意事项1.pthread_join 只能回收结合态线程对分离态线程调用 pthread_join 会直接失败返回 EINVAL 错误。2.线程属性对象可复用初始化后的 pthread_attr_t 可以多次用于创建线程用完再销毁即可。3.主线程退出会杀死所有子线程即使是分离态线程如果主线程提前 exit()所有子线程都会被强制终止所以主线程需要确保子线程执行完毕。4.线程退出方式pthread_exit()仅结束当前线程不影响其他线程return在线程函数中 return 等价于 pthread_exit()exit()结束整个进程所有线程都会终止4.线程控制函数1pthread_detach 分离线程项目内容所需头文件#include pthread.h原型int pthread_detach(pthread_t thread);功能将一个已创建的线程标记为分离态线程结束后系统自动回收资源参数thread要设置为分离态的线程 ID返回值成功返回0失败返回非 0 错误码与 pthread_attr_setdetachstate 的区别pthread_attr_setdetachstate创建线程前通过属性设置为分离态。pthread_detach创建线程后将其转为分离态。2pthread_cancel 取消线程项目内容所需头文件#include pthread.h原型int pthread_cancel(pthread_t thread);功能向指定线程发送取消请求目标线程的处理方式取决于取消状态和类型参数thread要取消的线程 ID返回值成功返回0失败返回非 0 错误码3pthread_setcancelstate 设置取消状态项目内容所需头文件#include pthread.h原型int pthread_setcancelstate(int state, int *oldstate);功能设置调用线程的可取消状态参数state新的取消状态-PTHREAD_CANCEL_ENABLE可被取消默认-PTHREAD_CANCEL_DISABLE不可被取消参数oldstate用于保存之前的取消状态可用于恢复返回值成功返回0失败返回非 0 错误码4pthread_setcanceltype 设置取消类型项目内容所需头文件#include pthread.h原型int pthread_setcanceltype(int type, int *oldtype);功能设置调用线程的取消响应方式参数type新的取消类型-PTHREAD_CANCEL_DEFERRED延时取消默认线程执行到取消点时才退出-PTHREAD_CANCEL_ASYNCHRONOUS异步取消收到请求后立即退出参数oldtype用于保存之前的取消类型返回值成功返回0失败返回非 0 错误码(5) 关键解析取消点Cancel Point当线程处于延时取消模式时只有在调用特定系统函数如 sleep()、read()、write()、pthread_cond_wait() 等时才会响应取消请求并退出。取消线程的风险直接取消线程可能导致资源泄漏如未释放的锁、未关闭的文件句柄。建议配合清理函数 pthread_cleanup_push/pop 使用确保线程退出前能释放资源。6完整示例代码#include stdio.h #include stdlib.h #include pthread.h #include unistd.h // 线程清理函数线程被取消时自动调用 void cleanup(void *arg) { printf(线程清理释放资源参数%p\n, arg); } void *thread_func(void *arg) { // 注册清理函数栈式注册push和pop必须成对出现 pthread_cleanup_push(cleanup, (void*)0x1234); printf(子线程开始运行tid: %lu\n, (unsigned long)pthread_self()); // 设置取消类型为延时取消默认 int oldtype; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, oldtype); // 模拟耗时任务sleep是取消点 for (int i 0; i 5; i) { printf(子线程运行中...%d\n, i); sleep(1); } printf(子线程正常结束\n); // 弹出清理函数若线程正常结束清理函数不会被执行 pthread_cleanup_pop(0); return NULL; } int main() { pthread_t tid; pthread_create(tid, NULL, thread_func, NULL); sleep(2); // 让子线程运行一段时间 printf(主线程发送取消请求\n); pthread_cancel(tid); // 发送取消请求 // 等待线程结束并回收资源 void *retval; pthread_join(tid, retval); if (retval PTHREAD_CANCELED) { printf(线程已被取消\n); } else { printf(线程正常结束返回值%p\n, retval); } return 0; }7核心注意事项pthread_cancel 只是发送请求不是强制终止。线程是否响应、何时响应取决于其取消状态和类型。分离态线程也可以被取消但主线程无法通过 pthread_join 获取其退出状态。尽量避免异步取消它会导致线程在任意位置终止极易造成资源泄漏和状态不一致。清理函数 pthread_cleanup_push/pop 必须成对使用且必须写在同一函数的同一层级中否则会导致栈5.线程的通信方式方式核心作用适用场景特点Linux 常用 API共享全局 / 静态变量直接传递数据简单数据交互天然共享、效率最高存在数据竞争必须加锁保护直接读写全局变量互斥锁 mutex保证临界区同一时刻只一个线程进入多线程争抢共享资源只能互斥、不能等待简单易用pthread_mutex_lock/unlock条件变量 cond线程等待 / 唤醒条件不满足就阻塞生产者 - 消费者、任务队列必须配合互斥锁精准唤醒线程pthread_cond_wait/signal/broadcast信号量 semaphore资源计数、同步 互斥限流、多生产者多消费者可跨线程 / 跨进程支持设置资源个数sem_wait/sem_post读写锁 rwlock读共享、写独占读多写少场景配置、缓存并发读效率远高于互斥锁pthread_rwlock_rdlock/wrlock线程信号 / 事件异步通知、唤醒阻塞线程异常中断、事件触发偏向通知机制不适合传大量数据Linux线程信号WindowsEvent 事件传数据共享变量防竞争互斥锁等条件、排队干活条件变量限流计数信号量读多写少优化读写锁异步通知唤醒线程信号 / 事件五、总结1、pthread 线程生命周期 全景表格阶段核心函数功能关键参数 / 返回重要考点创建线程pthread_create创建一个新线程成功 0失败 错误码线程默认是可结合态joinable获取自身 IDpthread_self获取当前线程 ID返回pthread_t无失败线程内调用等待线程结束pthread_join阻塞等待线程退出、回收资源成功 0只能等待 joinable 线程设置分离态pthread_detach将线程改为分离态成功 0分离后不能 join系统自动回收线程退出pthread_exit终止当前线程可带返回值只退出线程不退出进程取消线程pthread_cancel发送取消请求成功 0不是立即杀死看取消状态设置取消状态pthread_setcancelstate允许 / 禁止被取消ENABLE / DISABLE默认允许取消设置取消类型pthread_setcanceltype延时 / 异步取消DEFERRED / ASYNCHRONOUS默认延时取消安全线程属性初始化pthread_attr_init初始化属性对象成功 0创建分离态线程必须用设置分离属性pthread_attr_setdetachstate创建前指定分离 / 结合JOINABLE / DETACHED比 detach 更早设置销毁属性pthread_attr_destroy释放属性资源成功 0使用完必须销毁2、线程 生命周期流程图文字版主线程 ↓ pthread_create ——→ 创建子线程新建 → 就绪 → 运行 ↓ 子线程开始执行 ↓ 子线程运行中 - 可调用 pthread_self 获取自己ID - 可设置 setcancelstate / setcanceltype ↓ 子线程两种结束方式 1. 正常结束 → return / pthread_exit 2. 被取消 → pthread_cancel到达取消点退出 ↓ 线程结束后 ├── 若为 JOINABLE默认→ 必须 pthread_join 回收 └── 若为 DETACHED → 系统自动回收无需 joinjoinable 必须 join否则资源泄漏detached 不能 join系统自动清理cancel 只是发请求不是立即杀死默认延时取消3、完整示例#include stdio.h #include pthread.h #include unistd.h // 线程运行函数 void* func(void* arg) { int id *(int*)arg; // 获取自身线程ID pthread_t tid pthread_self(); printf(线程%d 运行tid%lu\n, id, (unsigned long)tid); // 设置允许取消默认就是允许演示用 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); // 设置延时取消默认安全 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); sleep(3); // 取消点 printf(线程%d 正常结束\n, id); return NULL; } int main() { pthread_t tid; int num 1; // 1. 创建线程 pthread_create(tid, NULL, func, num); sleep(1); // 2. 取消线程 pthread_cancel(tid); // 3. 等待回收必须join因为默认joinable void* ret; pthread_join(tid, ret); if (ret PTHREAD_CANCELED) printf(线程已被取消\n); return 0; }