当前位置: 首页 > news >正文

pthread_create通过加锁设置线程启动竞争条件

#include<pthread.h>template<class thd,class DATA>intManage<thd,DATA>::attachthread(thd&t){//设置系统级作用域和分离状态//设置系统级作用域 (system scope)含义:告诉操作系统,这个新创建的线程应该在整个系统范围内去竞争 CPU 资源,而不是只在当前进程内部竞争。//设置分离状态 (detached status)含义:将线程设置为“分离(Detached)”状态。//这意味着当这根线程运行结束时,操作系统会自动回收它所占用的所有资源(如内存、线程栈)。//主线程不需要、也不能调用 pthread_join() 来等待它结束或手动释放它。//对应的预期代码:在通常的 POSIX 线程开发中,这句注释后面应该还会紧跟一句 pthread_attr_setdetachstate(&attribute, PTHREAD_CREATE_DETACHED);。//声明一个 POSIX 线程属性结构体变量 attribute。像一个“配置清单”,用来存放线程的各种初始化设置。pthread_attr_tattribute;//初始化这个属性结构体,将其所有配置项设置为系统的默认值。pthread_attr_init(&attribute);//将线程的“绑定范围(Scope)”设置为系统级竞争(System Scope)。//该线程将直接与整个操作系统中的所有其他线程共同竞争 CPU 时间片。//这保证了线程能够获得更公平、更高效的系统资源调度(与之相对的是 PTHREAD_SCOPE_PROCESS,即仅在当前进程内部竞争资源)。pthread_attr_setscope(&attribute,PTHREAD_SCOPE_SYSTEM);// PTHREAD_CREATE_DETACHED宏值为1,设置线程的属性为分离状态(Detached)// 线程结束后系统自动回收所有资源, 无需(也不能)调用 pthread_join()// 无法通过pthread_join()获取返回值,"发射后不管"的后台任务pthread_attr_setdetachstate(&attribute,1);/* 主线程 │ ├─── pthread_create() ──► 子线程(DETACHED) │ │ │ (继续执行,无需等待) │ 运行中... │ │ │ 线程结束 │ └─► OS 自动回收内存/栈资源 │ └─ 无需 pthread_join() 这行代码的作用是:将即将创建的线程配置为"自生自灭"模式——线程运行完毕后,操作系统自动清理其占用的所有资源,主线程完全不需要介入管理。这在线程管理器(Thread Manager)这类场景中非常常见,适合大量创建、不需要等待结果的后台工作线程。 */// 创建子线程前锁定该线程对象关联的互斥锁(Mutex),// 以实现主线程与子线程的同步启动,防止因竞态条件(Race Condition)导致死锁.// 会调用pthread_mutex_lock(POSIX线程库的标准函数),阻塞当前线程,直到成功获取互斥锁为止。返回 0表示成功//如果锁已被其他线程持有,当前线程会阻塞等待,直到对方释放/* MyMutex mtx; if (mtx.lock()) { // 安全访问共享资源 mtx.release(); // 之后要对应释放 } else { // 处理加锁失败的情况 } ### 1. 避免信号丢失引起的死锁(竞态条件) 如果在 `pthread_create` 之前不进行 ` t.mutex_acquire_lock();` 加锁,可能会发生如下情况: 1. **主线程**调用 `pthread_create` 启动子线程。 2. **子线程**迅速启动并执行 [线程.cpp文件] 中的 `start_routine()` 入口函数。它调用 `mutex_acquire_lock()`(此时因为没加锁,会直接成功),接着调用 `thread_func()` 完成初始化,将状态置为 `THD_STATE_RUNNING`。 3. **子线程**调用 `t->signal_Condition_Lock()` 触发条件变量通知,随后释放锁。 4. 随后**主线程**才执行到 `t.wait_Condition_Lock()`(开始等待子线程的通知)。因为子线程的通知信号在主线程开始等待前就已经发送完毕,导致**主线程错过了该信号,从而无限期挂起(死锁)**。 ### 2. 加锁后的安全同步逻辑 主线程在 `pthread_create` 之前锁定互斥锁,可以强制实现安全的**“双向握手”**同步: sequenceDiagram participant Parent as 主线程 (Manage::attachthread) participant Child as 子线程 (start_routine) Note over Parent: 1. t.mutex_acquire_lock() (持有锁) Parent->>Child: 2. pthread_create() (创建子线程) Note over Child: 3. 运行并尝试 t->>mutex_acquire_lock()<br/>(因主线程持有锁而阻塞等待) // wait_Condition_Lock线程等待条件变量时使用的互斥锁 Note over Parent: 4. t.wait_Condition_Lock()<br/>(原子性释放锁并进入等待) Note over Child: 5. 成功获取锁,继续执行 Note over Child: 6. 初始化并置状态为 THD_STATE_RUNNING Child->>Parent: 7. t->>signal_Condition_Lock() (发送唤醒信号) Note over Child: 8. t->>wait_Condition_Lock() (释放锁并等待主线程允许) Note over Parent: 9. 主线程被唤醒,重新持有锁 Parent->>Child: 10. t.signal_Condition_Lock() & mutex_free_lock() (通知子线程并释放锁) Note over Child: 11. 子线程唤醒,继续进入运行循环 ### 总结 第xx行的 `t.mutex_acquire_lock();` 确保了**子线程即使被创建,也必须等待主线程完全进入 `wait_Condition_Lock()` 挂起等待状态后,才能开始执行初始化和发送通知**。这保证了条件变量通知信号绝不会丢失,确保了线程启动的安全性。 ---------------- */t.mutex_acquire_lock();// 无论当前代码把 threadid 当有符号还是无符号处理,0xFFFFFFFF 都能正确表示"无效",兼容两种场景。// 销毁线程时重置 destroy_thread(thread_id); thread_id = INVALID_THDID; 标为无效,防止 use-after-freepthread_tthreadid=0xFFFFFFFF;THREAD*pt=dynamic_cast<THREAD*>(&t);//使用默认attributeinttemp=pthread_create(&thdid,&attribute,(_thd_func*)thread_func,(void*)pt);if(temp!=0){// 创建失败pthread_attr_destroy(&attribute);t.mutex_free_lock();returntemp;}//查看线程状态t.wait_Condition_Lock();if(t.tStatus()!=THD_RUNNING_STATE){//失败,超时,没有运行pthread_attr_destroy(&attribute);t.signal_Condition_Lock();t.mutex_free_lock();return-1;}//父线程创建子线程后,通知子线程可以继续执行,并且释放某个互斥锁。//父线程确认子线程已经进入运行状态后,通过条件变量唤醒子线程,然后释放互斥锁,让子线程继续往下执行。t.signal_Condition_Lock();t.mutex_free_lock();pthread_attr_destroy(&attribute);// printf("线程manage成功运行");return0;}

这段代码是Manage::attachthread()的完整实现,负责创建并启动一个线程,并等待确认线程真正运行起来。下面逐层分析。


整体流程图

进入 attachthread() ↓ 初始化线程属性(attribute) ↓ 锁定互斥锁(Mutex_acquire_lock) ↓ pthread_create() 创建线程 ↓ 失败 → 解锁 → 返回错误码 ↓ 成功 等待子线程发信号(wait_Condition_Lock) ↓ 超时/未运行 → 解锁 → 返回错误 ↓ 确认运行 通知子线程继续(signal_Condition_Lock) 解锁(mutex_free_lock) 返回 0

逐段详细分析

1. 线程属性设置

pthread_attr_t attribute;pthread_attr_init(&attribute);pthread_attr_setscope(&attribute,PTHREAD_SCOPE_SYSTEM);pthread_attr_setdetachstate(&attribute,PTHREAD_CREATE_DETACHED);
属性含义
scopePTHREAD_SCOPE_SYSTEM线程与系统所有进程竞争 CPU,调度更公平
detachstatePTHREAD_CREATE_DETACHED线程结束后自动释放资源,无需pthread_join()

设置为detached(分离)意味着主线程不会等待这个线程结束,线程自己管理自己的生命周期。


2. 加锁 + 创建线程

t.mutex_acquire_lock();pthread_t tid=0xffffffff;THREAD*pt=dynamic_cast<THREAD*>(&t);inttemp=pthread_create(&tid,&attribute,(_thd_func*)thread_func,(void*)pt);

为什么创建前先加锁?

这是一个经典的**“先锁后创建”**同步技巧,防止子线程启动太快,在父线程调用waitCondLock()之前就发出了信号,导致信号丢失。时序如下:

父线程 子线程 mutex_acquire_lock() pthread_create() ──---───────→ 子线程启动 wait_Condition_Lock() 子线程尝试获取同一把锁(被阻塞) (等待信号) 父线程 wait_Condition_Lock 内部释放锁 子线程获得锁,初始化完成 子线程发信号 signal_Condition_Lock() 收到信号,继续执行

dynamic_cast的作用:

THREAD*pt=dynamic_cast<THREAD*>(&t);

将引用t安全地转回指针,传给pthread_create。因为THREAD可能有继承关系,dynamic_caststatic_cast更安全。


3. 创建失败处理

if(0!=temp){// 失败pthread_attr_destroy(&attribute);t.free_mutex_lock();returntemp;}

pthread_create失败时返回非 0 的错误码(如EAGAIN资源不足),这里做了:

  • 记录日志
  • 销毁 attribute 释放资源
  • 解锁(必须,否则死锁)
  • 返回错误码

4. 等待子线程确认启动

t.wait_Condition_Lock();if(t.tStatus()!=THD_RUNNING_STATE){//不是运行状态pthread_attr_destroy(&attribute);t.signal_Condition_Lock();t.free_mutex_lock();return-1;}

这是整段代码最关键的设计:父线程不是创建完就走,而是等待子线程回报"我已经跑起来了"

  • wait_Condition_Lock():阻塞等待条件变量,内部会释放 mutex,让子线程能执行
  • tStatus() != THD_RUNNING_STATE:子线程应该在启动后把状态设为THD_RUNNING_STATE,如果不是(比如超时),说明线程启动异常

失败时额外调用signal_Condition_Lock()是为了防止子线程还在等待,避免子线程永远阻塞。


5. 成功收尾

t.signal_Condition_Lock();t.free_mutex_lock();pthread_attr_destroy(&attribute);return0;
  • 通知子线程"父线程已确认,你可以继续执行"
  • 解锁、销毁 attribute、返回成功

注释里的彩蛋

说明加锁逻辑是后来为了修复线程启动竞争条件(Race Condition)而加入的。这类注释在老项目中很常见,记录了代码演化的历史。


总结:设计亮点

这段代码最值得学习的地方是双重同步机制,用互斥锁 + 条件变量解决了线程启动的竞态问题,确保attachthread()返回时线程一定已经真正在运行,而不只是"被创建了"。

http://www.cnnetsun.cn/news/3074092.html

相关文章:

  • 如何高效使用Diablo Edit2:暗黑破坏神2存档编辑器的完整指南
  • 查新报告分为哪几种?科技查新、查收查引与专利查新区别
  • Advanced XRay技术深度解析:如何通过方块渲染优化实现高效矿石定位
  • 5分钟免费让Windows拥有macOS优雅鼠标指针的完整指南
  • 用Backtrader回测DMI指标:一个Python量化新手的实战踩坑记录(附完整代码)
  • 基于sigrity的TDR/TDT仿真设计
  • 数据安全检查,这3个API盲区最容易被问穿
  • 如何用中文工作流轻松玩转AI绘画?这份完整指南让你从入门到精通
  • 第018章:ComfyUI文生图Z-Image模型创建数字人模特(二)
  • 01 静态分析(Static Analysis)
  • PKMS+AppOps 双权限体系:隐私管控、特权白名单全流程源码剖析
  • 2026年桌面风扇类型选购要点:从四个核心部件看懂一台风
  • Java实现字符串匹配:别再让算法理论画饼,实际应用才是王道
  • 把 ES Repository 纳入 CMS 轨道,一套更稳的 SAP PI 内容传输治理方式
  • Bebas Neue:开源字体设计的几何美学革命
  • 与你的 Elasticsearch 数据对话:使用 Google ADK 和 MCP 构建一个实时语音 agent ,分为 3 个组件
  • 从零理解 RAG:把“向量化“和“检索“讲成人话
  • 怎么用AI做历史课件视频?用 seedance2.0 制作趣味历史微课实战教程与对比
  • 机顶盒B860AV2.1-M刷机攻略
  • 高效XPath定位神器:xpath-helper-plus深度解析与实战指南
  • Java volatile 关键字相关用法总结:面试版详解
  • MYSQL--查询的执行流程
  • PC大型3A 角色扮演游戏(RPG)《怪物猎人物语3:命运双龙》网盘下载 免BIOS 中文版
  • 极低成本 AI 服务:独立开发者的多模型混合路由与流量网关设计
  • Python判断数字?别被isdigit()坑了!浮点负数全阵亡
  • UE5 插件版本 - PS添加PostProcess Pass
  • Beyond Compare 5永久激活:3步解决文件对比工具授权限制
  • Appium 移动端自动化环境搭建(Android/iOS)
  • YOLO26N 姿态估计模型训练全流程
  • 英雄联盟国服免费换肤完全指南:5分钟掌握R3nzSkin终极技巧