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
  • 关键特性:
  1. 阻塞函数:调用线程会一直阻塞,直到目标线程执行完毕。
  2. 资源回收:默认属性(非分离)的线程退出后,资源不会自动释放,必须通过 pthread_join 回收,否则会造成内存泄漏。
  3. 状态变更:join 后线程会自动变为分离状态。
  4. 失败场景:若目标线程已分离,调用 pthread_join 会直接失败。
  5. 参数说明:retval用于接收线程的退出码,不需要可传NULL。

3. 线程分离(Detach)

(1)两种分离方式

  • 方式一:创建后主动分离(常用)
  1. 函数:int pthread_detach(pthread_t thread);
  2. 说明:成功返回 0,建议紧跟在pthread_create后使用
  3. 效果:线程与主线程断开联系,结束后自动释放资源,不会产生僵尸线程,无法再被 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);
  1. PTHREAD_CANCEL_ENABLE(默认):允许线程响应取消请求。
  2. PTHREAD_CANCEL_DISABLE:禁用取消,取消请求会被挂起,直到重新启用。
  3. 发送的取消命令会被挂起,直到线程恢复可取消状态。

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)回调函数触发条件(面试高频)

满足以下任意一种情况,清理函数会自动执行:

  1. 线程被pthread_cancel取消
  2. 线程调用pthread_exit主动退出
  3. 调用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、通信协议及开发工具链等核心内容。

全部评论

相关推荐

点赞 评论 收藏
分享
评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务