多线程程序如何安全退出
/* * sm_demo.c * 模拟 st_sm_deinit 的三种场景 * * 编译: gcc sm_demo.c -o sm_demo -lpthread * 运行: * ./sm_demo 1 worker 已经自己退出deinit 直接收尾 * ./sm_demo 2 worker 优雅退出deinit 等到后 join * ./sm_demo 3 worker 退不出deinit 超时后 detach * */ #include stdio.h #include stdlib.h #include string.h #include pthread.h #include unistd.h #include errno.h /* ---------------- 可调参数 ---------------- */ #define SM_DEINIT_WAIT_MS 1000 /* deinit 最多等 1 秒 */ #define SM_DEINIT_SLICE_MS 10 /* 每 10ms 查一次 */ /* ---------------- 全局状态机 ---------------- */ typedef struct { pthread_mutex_t lock; pthread_t worker; int worker_alive; int abort_req; int mode; /* 1/2/3决定 worker 行为 */ } sm_t; static sm_t g_sm; /* ---------------- worker 线程 ---------------- */ static void *worker_thread(void *arg) { (void)arg; printf([worker] start, mode%d\n, g_sm.mode); pthread_mutex_lock(g_sm.lock); g_sm.worker_alive 1; pthread_mutex_unlock(g_sm.lock); if (g_sm.mode 1) { /* 场景 1worker 干完活自己退出不等 abort */ printf([worker] mode 1: work 300ms then exit by itself\n); usleep(300 * 1000); } else if (g_sm.mode 2) { /* 场景 2worker 检查 abort收到就退 */ printf([worker] mode 2: wait for abort\n); while (1) { pthread_mutex_lock(g_sm.lock); int stop g_sm.abort_req; pthread_mutex_unlock(g_sm.lock); if (stop) break; usleep(10 * 1000); } printf([worker] mode 2: got abort, exit\n); } else { /* 场景 3worker 忽略 abort永远不退 */ printf([worker] mode 3: ignore abort, never exit\n); while (1) { usleep(10 * 1000); } } pthread_mutex_lock(g_sm.lock); g_sm.worker_alive 0; pthread_mutex_unlock(g_sm.lock); printf([worker] exit\n); return NULL; } /* ---------------- 初始化 ---------------- */ static int sm_init(int mode) { memset(g_sm, 0, sizeof(g_sm)); g_sm.mode mode; pthread_mutex_init(g_sm.lock, NULL); if (pthread_create(g_sm.worker, NULL, worker_thread, NULL) ! 0) { perror(pthread_create); return -1; } printf([init] worker created\n); return 0; } /* * sm_deinit —— 完全照搬 st_sm_deinit 的结构 * */ static void sm_deinit(void) { int i 0; int alive 0; pthread_t th 0; /* 第 1 段设 abort_req读出状态 */ pthread_mutex_lock(g_sm.lock); g_sm.abort_req 1; alive g_sm.worker_alive; th g_sm.worker; pthread_mutex_unlock(g_sm.lock); printf([deinit] alive%d, th%lu\n, alive, (unsigned long)th); /* 第 2 段worker 已死直接收尾 */ if (!alive) { if (th) { (void)pthread_join(th, NULL); pthread_mutex_lock(g_sm.lock); g_sm.worker 0; pthread_mutex_unlock(g_sm.lock); printf([deinit] joined dead worker\n); } pthread_mutex_destroy(g_sm.lock); printf([deinit] path 2: worker already dead, done\n); return; } /* 第 3 段轮询等待 */ printf([deinit] waiting for worker (max %d ms)...\n, SM_DEINIT_WAIT_MS); for (i 0; i SM_DEINIT_WAIT_MS / SM_DEINIT_SLICE_MS; i) { pthread_mutex_lock(g_sm.lock); alive g_sm.worker_alive; pthread_mutex_unlock(g_sm.lock); if (!alive) break; usleep(SM_DEINIT_SLICE_MS * 1000u); } /* 第 4 段等到了join 销毁锁 */ pthread_mutex_lock(g_sm.lock); if (!g_sm.worker_alive g_sm.worker) { th g_sm.worker; g_sm.worker 0; pthread_mutex_unlock(g_sm.lock); (void)pthread_join(th, NULL); pthread_mutex_destroy(g_sm.lock); printf([deinit] path 4: worker exited gracefully, done\n); return; } /* 第 5 段没等到detach 兜底 */ if (g_sm.worker) { (void)pthread_detach(g_sm.worker); g_sm.worker 0; g_sm.worker_alive 0; } pthread_mutex_unlock(g_sm.lock); printf([deinit] path 5: worker still busy, detached for process exit\n); } /* ---------------- main ---------------- */ int main(int argc, char *argv[]) { if (argc 2) { printf(usage: %s 1|2|3\n, argv[0]); printf( 1: worker exits by itself before deinit\n); printf( 2: worker exits gracefully on abort\n); printf( 3: worker never exits, deinit detaches\n); return 1; } int mode atoi(argv[1]); if (mode 1 || mode 3) { printf(mode must be 1/2/3\n); return 1; } printf( mode %d \n, mode); if (sm_init(mode) ! 0) return 1; /* 不同场景等待时间不同 */ if (mode 1) { /* 等 worker 自己死 */ sleep(1); } else if (mode 2) { /* 等 worker 起来 */ sleep(1); } else { /* 等 worker 起来 */ sleep(1); } printf([main] call sm_deinit\n); sm_deinit(); printf([main] done\n); return 0; }代码里面的知识点一张图总结为什么进程要退出worker 可能还在跑 │ ▼ 不能直接杀丢数据、坏状态 │ ▼ 优雅退出通知 等待 收尾 │ ├─ 通知 → abort_req ├─ 知状态 → worker_alive加锁保护 ├─ 等待 → 轮询 超时 ├─ 等到了 → join 销毁锁 └─ 等不到 → detach 兜底不销毁锁

相关新闻

Java后端面试实战:从HashMap到JVM的深度追问与考点拆解

Java后端面试实战:从HashMap到JVM的深度追问与考点拆解

1. 一场让我印象深刻的面试:严肃面试官和"谢飞机"的较量去年这个时候,我作为技术面试官参与了某家头部互联网公司的Java后端岗位招聘。简历筛选阶段,一份背景普通的简历引起了我的注意——没有名校光环,没有大厂实习&am…

2026/10/11 5:41:50 阅读更多 →
无法生成:‘rea‘缺乏有效技术语义与上下文

无法生成:‘rea‘缺乏有效技术语义与上下文

项目标题“rea”目前未提供有效上下文、正文描述、关键词列表或摘要说明,仅有一个孤立的字符串“rea”,且附带的搜索内容为空(),无任何可解析的语义信息、领域指向、技术线索或应用场景提示。在严格遵循你设定的全部创…

2026/10/11 5:40:49 阅读更多 →
小场地游乐项目怎么选?沙盘赛车十几平就能开

小场地游乐项目怎么选?沙盘赛车十几平就能开

很多商场、社区商业和亲子空间,都有那么一块几十平的闲置空地。招商一直在找小面积、低门槛、还能持续引流的项目。沙盘赛车这种FPV体验,就能搭一条迷你沙盘赛道,不用层高、不用大承重,普通室内商铺就能上。超元力FPV沙盘赛车&…

2026/10/11 5:40:49 阅读更多 →

最新新闻

电商用户聚类实战:基于RFM特征与K-means的用户分群指南

电商用户聚类实战:基于RFM特征与K-means的用户分群指南

1. 为什么要用聚类做用户分析:从拍脑袋分群到数据驱动先还原一个我真实遇到的场景。前阵子帮一家电商团队做用户运营支持,运营同事找我提需求:"把我们用户分成高、中、低三类,重点维护高的"——我问怎么定义高&#xff…

2026/10/11 6:22:15 阅读更多 →
COMSOL压电超声换能器一发一收全链路仿真指南

COMSOL压电超声换能器一发一收全链路仿真指南

做压电超声仿真这阵子,我几乎把COMSOL的声固耦合和压电耦合模块翻了个底朝天。起因很简单,要模拟一个一发一收的超声换能器系统:发射端压电陶瓷通电振动,声波经过介质传播,接收端再把声压转回电信号。这个“一端发射一…

2026/10/11 6:22:15 阅读更多 →
AI智能体赋能电磁仿真:HFSS/CST本地化部署与自动化优化实战

AI智能体赋能电磁仿真:HFSS/CST本地化部署与自动化优化实战

做电磁仿真这些年,我最深的体会是:模型要反复调,参数要反复扫,后处理要反复导,真正留给"思考怎么设计"的时间反而少得可怜。最近我把 AI 智能体这套思路搬到了 HFSS 和 CST 的全波仿真流程里,让大…

2026/10/11 6:22:15 阅读更多 →
Agent——8.AI智能体构建:用CoT与ReAct拆解OpenManus式任务规划

Agent——8.AI智能体构建:用CoT与ReAct拆解OpenManus式任务规划

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 6:22:15 阅读更多 →
2025值得推荐的AI生产力工具一览表(2):用TaoToken统一Key打通Cursor与Claude Code

2025值得推荐的AI生产力工具一览表(2):用TaoToken统一Key打通Cursor与Claude Code

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 6:22:15 阅读更多 →
PS5游戏存档工具开发与跨平台迁移实践

PS5游戏存档工具开发与跨平台迁移实践

我无法基于当前输入生成符合要求的博文。原因如下:项目标题“AnyPS5”缺乏明确指向性,未说明是硬件改装、模拟器方案、跨平台兼容层、游戏存档工具、远程串流方案,还是其他技术方向;项目正文为空,无任何功能描述、技术…

2026/10/11 6:21:14 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →