news 2026/3/23 4:31:01

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/3/15 14:43:38

零基础也能懂的电源管理系统概述

电源管理&#xff1a;不只是“供电”那么简单你有没有想过&#xff0c;为什么你的手机能一边充电、一边快充、一边还能正常运行&#xff1f;为什么一块小小的电池能让智能手表连续工作好几天&#xff1f;为什么有些设备一开机就死机&#xff0c;而另一些却稳定如初&#xff1f;…

作者头像 李华
网站建设 2026/3/21 6:31:19

LCD12864并行模式新手教程:基础接线与测试

从零开始玩转 LCD12864&#xff1a;并行驱动实战全记录你有没有遇到过这样的情况&#xff1f;花几十块买了一块看起来挺“高级”的图形屏&#xff0c;接口密密麻麻&#xff0c;接上单片机后却只看到一片黑——既没字也没图&#xff0c;连个光标都不闪。别急&#xff0c;这几乎是…

作者头像 李华
网站建设 2026/3/15 2:24:33

最大似然估计简介

原文&#xff1a;towardsdatascience.com/introduction-to-maximum-likelihood-estimates-7e37f83c6757 简介 最大似然估计&#xff08;MLE&#xff09;是一种基本方法&#xff0c;它使任何机器学习模型都能从可用数据中学习独特的模式。在这篇博客文章中&#xff0c;我们将通…

作者头像 李华
网站建设 2026/3/15 18:45:29

Qwen3-VL浏览GitHub镜像库查找最新AI项目

Qwen3-VL 浏览 GitHub 镜像库查找最新 AI 项目 在多模态 AI 技术飞速演进的今天&#xff0c;开发者面临的不再是“有没有模型可用”&#xff0c;而是“如何快速试用、验证并集成前沿能力”。传统方式下&#xff0c;下载百亿参数模型动辄耗费数小时&#xff0c;环境配置复杂、依…

作者头像 李华
网站建设 2026/3/20 15:32:40

STLink驱动安装操作指南:适用于Windows系统

STLink驱动安装全攻略&#xff1a;从零搞定Windows下的调试连接 在STM32开发的世界里&#xff0c;你可能写过无数行代码、调通过复杂的外设驱动&#xff0c;但最让人抓狂的往往不是程序逻辑&#xff0c;而是—— 电脑连不上STLink调试器 。 插上开发板&#xff0c;打开IDE&…

作者头像 李华
网站建设 2026/3/19 21:20:41

零基础也能懂的nrf52832的mdk下载程序教程

从零开始玩转nRF52832&#xff1a;Keil MDK下载程序全解析&#xff0c;不只是“点一下”那么简单 你有没有过这样的经历&#xff1f; 明明代码写好了&#xff0c;工程也编译通过了&#xff0c;信心满满地点击 Keil 的“Download”按钮&#xff0c;结果弹出一串红字&#xff1…

作者头像 李华