news 2026/5/1 5:09:26

Linux 线程编程 - 线程取消:取消状态 + 取消类型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 线程编程 - 线程取消:取消状态 + 取消类型

在 Linux 多线程编程中,经常遇到 “主线程需要主动终止子线程执行” 的场景 —— 比如子线程处理任务超时、业务逻辑需要中断工作线程,这时线程取消(pthread_cancel)就是核心解决方案!本文整理线程取消的核心概念、关键接口和实战代码,零基础也能看懂。

一、核心特点

1. 适用场景

  • 主线程需要主动终止子线程执行(如超时任务终止、批量线程按需停止);
  • 线程执行耗时操作时,需响应外部终止指令;
  • 替代 “暴力杀死线程” 的方式,实现 “协商式” 线程终止。

2. 核心逻辑

线程取消是 “通知式” 操作,而非 “强制杀死”,目标线程的执行逻辑由两个核心属性决定:

核心属性作用可选值
取消状态决定是否接收取消请求① PTHREAD_CANCEL_ENABLE(默认,允许接收)② PTHREAD_CANCEL_DISABLE(拒绝接收)
取消类型决定接收请求后何时终止① PTHREAD_CANCEL_DEFERRED(默认,延迟取消,需执行到 “取消点” 才终止)② PTHREAD_CANCEL_ASYNCHRONOUS(立即取消,收到请求后马上终止)

关键概念:取消点系统预定义的 “触发取消” 的函数,延迟取消(DEFERRED)仅在执行这些函数时响应取消请求,常见取消点:pthread_joinpthread_cond_waitsem_waitsleepreadwrite等(大部分阻塞型系统调用)。

二、关键接口

接口功能关键说明
pthread_setcancelstate设置线程取消状态参数 1:新状态(ENABLE/DISABLE);参数 2:存储旧状态(可传 NULL);成功返回 0,失败返回错误码
pthread_setcanceltype设置线程取消类型参数 1:新类型(DEFERRED/ASYNCHRONOUS);参数 2:存储旧类型(可传 NULL);成功返回 0,失败返回错误码
pthread_cancel向目标线程发送取消请求参数:目标线程 ID;仅发送请求,不等待线程终止;成功返回 0,失败返回错误码
pthread_join等待线程终止并回收资源发送取消请求后,需通过该接口回收线程资源,避免僵尸线程

三、实战代码

模拟场景:主线程创建子线程后,等待 5 秒发送取消请求,子线程设置 “允许取消 + 延迟取消”,执行循环任务并响应取消请求:

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> void* thread_function(void* arg) { // 1. 设置取消状态:允许接收取消请求 int res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if (res != 0) { perror("Thread pthread_setcancelstate failed"); exit(1); } // 2. 设置取消类型:延迟取消(默认,可省略,此处显式配置) res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); if (res != 0) { perror("Thread pthread_setcanceltype failed"); exit(1); } printf("thread_function is running\n"); // 循环执行任务(sleep是取消点,触发延迟取消) for (int i = 0; i < 10; i++) { printf("Thread is still running (%d)...\n", i); sleep(1); // 取消点:执行到此处时响应取消请求 } // 若未被取消,正常退出 pthread_exit(0); } int main() { // 1. 创建子线程 pthread_t a_thread; int res = pthread_create(&a_thread, NULL, thread_function, NULL); if (res != 0) { perror("Thread creation failed"); exit(1); } // 2. 主线程等待5秒,模拟业务逻辑后发送取消请求 sleep(5); printf("Canceling thread...\n"); res = pthread_cancel(a_thread); if (res != 0) { perror("Thread cancelation failed"); exit(1); } // 3. 等待子线程终止并回收资源 printf("Waiting for thread to finish...\n"); void* thread_result; res = pthread_join(a_thread, &thread_result); if (res != 0) { perror("Thread join failed"); exit(1); } printf("Thread canceled successfully\n"); return 0; }

四、编译运行

线程取消依赖 pthread 库,编译时必须加-lpthread

# 编译代码 gcc thread_cancel.c -o thread_cancel -lpthread # 运行程序 ./thread_cancel

运行结果说明

thread_function is running Thread is still running (0)... Thread is still running (1)... Thread is still running (2)... Thread is still running (3)... Thread is still running (4)... Canceling thread... Waiting for thread to finish... Thread canceled successfully

子线程执行 5 次循环后,主线程发送取消请求,子线程在第 5 次 sleep(取消点)时响应取消,终止执行。

五、踩坑

  1. 取消状态设为 DISABLE 时发送请求:取消请求会被忽略,线程继续执行直到正常退出;
  2. 延迟取消无取消点:若线程执行无取消点的死循环(如while(1);),即使发送取消请求也无法终止;
  3. 未调用 pthread_join 回收资源:发送取消请求后,线程终止但资源未回收,产生僵尸线程;
  4. 立即取消(ASYNCHRONOUS)的风险:线程可能在任意时刻终止,易导致资源泄漏(如未释放 malloc 的内存、未关闭文件描述符);
  5. 忽略接口返回值:未校验 pthread_setcancelstate/type 的返回值,配置失败后无感知,线程行为不符合预期。

六、进阶扩展

1. 自定义取消点

若线程执行无系统取消点的逻辑(如纯计算循环),可通过pthread_testcancel()手动插入取消点:

// 子线程循环中插入自定义取消点 for (int i = 0; i < 1000000; i++) { // 纯计算逻辑,无系统取消点 int x = i * i; pthread_testcancel(); // 手动取消点:检查是否有取消请求,有则终止 }

2. 安全取消:清理资源

线程被取消前,可通过pthread_cleanup_push/pop注册清理函数,确保资源释放:

// 清理函数:释放资源 void cleanup_handler(void* arg) { printf("Cleaning up resources...\n"); free(arg); // 释放malloc的内存 } void* thread_function(void* arg) { char* buf = malloc(1024); // 注册清理函数(需成对使用push/pop) pthread_cleanup_push(cleanup_handler, buf); // 业务逻辑... sleep(1); // 注销清理函数(即使被取消,也会执行) pthread_cleanup_pop(1); // 参数1:执行清理函数;0:不执行 pthread_exit(0); }

七、总结

线程取消是 Linux 多线程 “协商式终止” 的核心机制,核心要点:

  1. 取消请求是 “通知”,目标线程需配置ENABLE状态才会响应;
  2. 延迟取消(DEFERRED)是安全选择,仅在取消点响应,便于资源清理;
  3. 立即取消(ASYNCHRONOUS)慎用,易导致资源泄漏;
  4. 发送取消请求后,必须通过pthread_join回收线程资源。

对比 “共享变量终止线程”(需线程主动检查变量),线程取消更高效,但需注意资源清理,是多线程按需终止的必备知识点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 0:50:27

Qwen3-VL分析Qwen3-VL-Quick-Start项目README文件

Qwen3-VL 技术解析与快速部署实践 在今天这个视觉信息爆炸的时代&#xff0c;AI 系统能否“看懂”一张图、一段视频&#xff0c;甚至一个复杂的用户界面&#xff0c;已经成为衡量其智能水平的关键标尺。传统语言模型面对图像时往往束手无策&#xff0c;而早期的多模态方案又常常…

作者头像 李华
网站建设 2026/4/27 14:35:32

搞懂malloc底层原理后,我用C++17 PMR写了一个5倍性能的分配器

malloc到底是怎么工作的? 不是那种"调用sbrk向系统申请内存"的泛泛而谈。我说的是真正的底层细节:chunk是怎么组织的?free之后内存去哪了?为什么有时候malloc很快,有时候又慢得离谱? 这些问题,只有啃过glibc malloc源码、自己动手实现过一遍,才能真正搞明白…

作者头像 李华
网站建设 2026/4/23 14:33:14

Qwen3-VL提取UltraISO注册码信息(教学场景展示)

Qwen3-VL提取UltraISO注册码信息&#xff08;教学场景展示&#xff09; 在日常软件使用中&#xff0c;我们常会遇到需要从一张截图里“读出”注册码的场景——比如帮助同事找回丢失的激活密钥、分析老旧系统的授权界面&#xff0c;或是教学环境中演示AI如何理解图形用户界面。这…

作者头像 李华
网站建设 2026/4/23 17:35:04

Sonic在短视频创作领域的三大典型应用场景

Sonic在短视频创作中的场景化实践与技术落地路径 你有没有想过&#xff0c;一个数字人主播可以24小时不间断地讲解产品、授课教学&#xff0c;甚至用不同语言向全球观众直播&#xff1f;这不再是科幻电影的桥段——随着AI生成技术的成熟&#xff0c;这样的场景正在真实发生。而…

作者头像 李华
网站建设 2026/4/28 1:44:12

rs485modbus协议源代码驱动开发:手把手教程(从零实现)

从零实现 RS485 Modbus RTU 驱动&#xff1a;手把手教你写一套能跑的源代码为什么我们要自己写 Modbus 驱动&#xff1f;在工业现场&#xff0c;你可能已经用过无数遍 Modbus 协议——读电表、控变频器、接温湿度传感器。但当你面对一个裸片 STM32 或者 ESP32&#xff0c;没有现…

作者头像 李华
网站建设 2026/4/23 15:29:15

儿童早教产品融合Sonic技术,增强互动趣味性

儿童早教产品融合Sonic技术&#xff0c;增强互动趣味性 在儿童教育领域&#xff0c;一个看似简单却长期困扰开发者的问题是&#xff1a;如何让学习内容“活”起来&#xff1f;传统的图文课件和预录视频虽然稳定可靠&#xff0c;但缺乏动态反馈与情感连接&#xff0c;难以持续吸…

作者头像 李华