5.5 Linux 应用开发 线程(二)
一、线程的基本控制
1. 线程的 3 种退出方式
(1)从启动函数 return 退出。
- 语法:return (void*)1;
- 说明:返回值作为线程退出码,仅正常退出时生效,不会触发线程清理函数(面试高频坑点)。
(2)被同进程其他线程取消。
- 函数:int pthread_cancel(pthread_t tid)。
- 返回值:成功返回 0,失败返回错误码。
- 核心前提:被取消线程必须有取消点。
取消点:线程遇到阻塞代码(如sleep、read、pthread_cond_wait等)时,会检查取消请求。
无阻塞代码的线程,需手动调用 pthread_testcancel( ) 设置取消点。
(3)主动调用 pthread_exit 退出。
- 函数:void pthread_exit(void *rval);
- 说明:rval 为线程退出码,会触发线程清理函数执行,是推荐的线程退出方式。
2. 等待回收线程(pthread_join)
对于一个默认属性的线程来说,线程占用的资源不会因其退出而得到释放。
- 函数:
int pthread_join(pthread_t tid, void **retval); - 返回值:成功返回 0
- 关键特性:
- 阻塞函数:调用线程会一直阻塞,直到目标线程执行完毕。
- 资源回收:默认属性(非分离)的线程退出后,资源不会自动释放,必须通过 pthread_join 回收,否则会造成内存泄漏。
- 状态变更:join 后线程会自动变为分离状态。
- 失败场景:若目标线程已分离,调用 pthread_join 会直接失败。
- 参数说明:retval用于接收线程的退出码,不需要可传NULL。
3. 线程分离(Detach)
(1)两种分离方式
- 方式一:创建后主动分离(常用)
- 函数:int pthread_detach(pthread_t thread);
- 说明:成功返回 0,建议紧跟在pthread_create后使用
- 效果:线程与主线程断开联系,结束后自动释放资源,不会产生僵尸线程,无法再被 join
- 方式二:创建时设置分离属性
pthread_attr_t attr; // 声明属性 pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 用该attr创建的线程默认分离
(2)分离线程核心特性(面试必背)
- 分离线程退出后自动释放资源,无需
pthread_join。 - 分离后不可再被
join,不可恢复为非分离状态。 - 动态分配的内存(如
malloc)仍需手动释放,不受分离属性影响。
4. 线程取消(Cancel)
(1) 取消核心逻辑
- 函数:
int pthread_cancel(pthread_t tid); - 本质:向目标线程发送取消请求,而非立即终止
- 生效条件:线程必须遇到取消点才会响应请求
(2)手动控制取消
- 手动设置取消点:
void pthread_testcancel(void); - 作用:在无阻塞的计算型线程中,手动插入取消检查点,让线程能响应取消请求
- 设置取消状态:
int pthread_setcancelstate(int state, int *oldstate);
- PTHREAD_CANCEL_ENABLE(默认):允许线程响应取消请求。
- PTHREAD_CANCEL_DISABLE:禁用取消,取消请求会被挂起,直到重新启用。
- 发送的取消命令会被挂起,直到线程恢复可取消状态。
5. 向线程发送信号(pthread_kill)
int pthread_kill(pthread_t tid, int sig); // 返回值:成功返回0,失败返回错误码(不会发送信号) // sig = 0(保留信号):不实际发送信号,仅用于检查目标线程是否存活(面试高频考点) // sig ≠ 0:向指定线程发送对应信号,需提前用signal()/sigaction()注册处理函数,否则按系统默认行为处理(通常终止进程)
- 关键特性:信号处理逻辑与进程信号完全一致,线程共享进程的信号处理函数。
6. 线程的清理程序(Cleanup Handler)
(1)注册清理函数
void pthread_cleanup_push(void (*routine)(void *), void *arg); // routine:线程退出时执行的清理回调(如释放锁、释放内存) // arg:传递给回调函数的参数 // 必须与pthread_cleanup_pop成对嵌套,否则编译报错
(2)弹出 / 执行清理函数
void pthread_cleanup_pop(int execute); // execute = 0:仅弹出栈,不执行清理函数 // execute ≠ 0:弹出栈并执行清理函数
(3)回调函数触发条件(面试高频)
满足以下任意一种情况,清理函数会自动执行:
- 线程被
pthread_cancel取消 - 线程调用
pthread_exit主动退出 - 调用
pthread_cleanup_pop(非0)主动触发
- 避坑点:线程从启动函数
return退出,不会触发清理函数!
二、线程同步
1. 互斥量
互斥量是线程同步最基础的工具,用于保证共享资源同一时间仅被一个线程访问,解决竞态条件问题。
(1)互斥锁初始化
- 方式 1:动态初始化(推荐,适用于动态分配 / 局部互斥量)
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); // mutex:待初始化的互斥量指针 // attr:互斥量属性,默认传NULL(默认属性) // 返回值:成功返回0
- 方式 2:静态初始化(适用于全局 / 静态互斥量)
无需手动销毁,进程退出时自动释放。
pthre
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++/嵌入式开发 秋招面经 文章被收录于专栏
一名985硕,在25年秋招中斩获多个C++/嵌入式开发Offer。本专栏将分享我的面经,涵盖C/C++、操作系统、计算机网络、ARM体系与架构、Linux应用/驱动开发、Qt、通信协议及开发工具链等核心内容。

