今天说说MFC的线程,当年用它实现中间件消息得心应手之时,可以实现一边实时接收数据,一边更新界面图表图文信息,顺滑得让人想吹声口哨。 MFC 多线程它像给程序装上了分身术,让原本只能 “单任务跑腿” 的代码,突然有了 双重任务 的本事。

一、线程的底层逻辑

设计模式里有个工厂模式,在我的眼里,进程就像一整个工厂:有独立的厂房(内存空间)、固定的设备(系统资源),是操作系统能调度的最小单位。而线程就是工厂里的工人—— 他们共享厂房里的工具(进程资源),但各干各的活,既能协作装配一台机器,也能分头处理不同订单。

比如你打开的 VC++6.0 是一个进程,里面敲代码的编辑窗口、实时编译的后台进程、甚至右下角的拼写检查提示,都是不同的线程在工作。线程有自己的优先级:就像工厂里紧急订单优先处理,高优先级线程(比如实时数据刷新)会抢占 CPU 资源,但如果一直让 “急单工人” 霸占设备,其他线程就会饿死 —— 这就是当年调试时总遇到的 “界面假死” 根源。

二、两类线程

MFC 里的线程分两类,就像工厂里的两种工人:

Worker Threads(工作线程) 是埋头干活的 “流水线工人”。他们不碰界面,专门处理后台任务 —— 比如我当年写的串口数据解析、文件批量转换。这类线程没有消息循环,干完活就下班,简单直接。

UI Threads(界面线程) 则是 “前台接待员”。他们有自己的消息循环(就像接待台的呼叫系统),能创建窗口、处理按钮点击等交互。当年做数据监控软件时,我用 UI 线程单独管理报警弹窗,就算主界面卡了,报警窗口仍能弹出来 —— 这是当时在C/S开发场景里可是能救命的设计。

三、CWinThread:MFC 给线程搭的 “脚手架”

MFC 用 CWinThread 类封装了线程操作,就像给工人准备了标准化工具包。创建线程不用直接调用 Windows API,通过它能少写很多重复代码。

比如创建一个工作线程,核心就两步:


// 1. 定义线程函数(工人要干的活)UINT DataProcessThread(LPVOID pParam){// pParam是传递的参数(比如数据指针)DataHandler* handler = (DataHandler*)pParam;while(handler->IsRunning()){handler->ProcessOnePacket(); // 处理一个数据包Sleep(10); // 让出CPU给其他线程}return 0; // 线程结束}// 2. 启动线程(招募工人)CWinThread* pThread = AfxBeginThread(DataProcessThread, // 线程函数&m_dataHandler, // 传递参数THREAD_PRIORITY_NORMAL, // 优先级0, // 栈大小(默认即可)CREATE_SUSPENDED, // 先挂起,准备好再运行NULL);if(pThread != NULL){pThread->m_bAutoDelete = TRUE; // 线程结束后自动释放pThread->ResumeThread(); // 开始工作}

这段代码里的 AfxBeginThread 是 MFC 的 “线程启动器”。当年总忘了设 m_bAutoDelete,结果线程结束后内存没释放,调试时看到内存占用越来越高,才明白 MFC 的 “自动清理” 有多重要。

UI 线程的创建稍复杂些,需要派生 CWinThread 子类,重写 InitInstance 函数初始化窗口。就像给接待员配专属工作台,得先把桌子椅子(窗口资源)准备好。

四、线程之间的协作

线程管理的精髓,在于 “该停的时候停好,该协作的时候不抢”。

早期不理解,写程序时,我粗暴地用 TerminateThread 强制结束线程,结果经常丢数据 —— 这就像突然关掉工厂电源,工人手里的零件肯定会散落一地。正确的做法是 “温柔通知”:用一个全局标志位告诉线程 “可以下班了”。


// 安全结束线程的例子class DataHandler{private:bool m_bRunning; // 线程运行标志public:DataHandler() : m_bRunning(false) {}void Start() { m_bRunning = true; }void Stop() { m_bRunning = false; } // 通知线程停止bool IsRunning() { return m_bRunning; }};

线程同步则是另一个大学问。多个线程抢着读写同一块数据时,就像两个工人抢一把扳手 —— 轻则数据错乱,重则程序崩溃。MFC 提供了 CCriticalSection(临界区)、CMutex(互斥量)等 “工具锁”,我最常用临界区,就像给扳手加个锁,谁用谁开锁,用完再还给别人。


// 用临界区保护共享数据CCriticalSection m_csData; // 定义临界区(锁)vector<DataPacket> m_packets; // 共享数据// 线程1:添加数据void AddPacket(DataPacket pkt){CSingleLock lock(&m_csData, TRUE); // 上锁m_packets.push_back(pkt);} // 离开作用域自动解锁// 线程2:读取数据vector<DataPacket> GetAllPackets(){CSingleLock lock(&m_csData, TRUE); // 上锁vector<DataPacket> temp = m_packets;m_packets.clear();return temp;}

卡过早期做股票行情软件的C++程序,看过K线图的处理情况,行情接收线程和 UI 刷新线程总抢着访问行情数据,用了临界区后,K 线图再也没出现过 “跳空” 的怪象。

最后小结

现在用 Python、Go 时,线程(协程)的用法变了很多,但每次处理并发,总会想起 MFC 多线程的日子。那些调试线程死锁的深夜,盯着调试器里的线程状态发呆;涉世之初时,爷爷不乏为少加一个锁导致程序在客户现场崩溃的愧疚;还有第一次用多线程让界面流畅运行时的兴奋 —— 这些经历比代码本身更珍贵。

MFC 多线程就像编程界的 “老自行车”,现在看来有些笨拙,但它教会我的道理从未过时:好的并发设计,不是让线程各自为战,而是让它们像默契的团队一样协作。就像工厂里的工人,各司其职又互相配合,才能高效运转。未完待续...........

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90043.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90043.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/90043.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高速公路自动化安全监测主要内容

近年来&#xff0c;随着社会经济的快速发展&#xff0c;高速公路的通车里程不断增加&#xff0c;交通流量日益增大。与此同时&#xff0c;高速公路交通事故数量也呈现出一定的增长趋势。这些事故不仅造成了大量的人员伤亡和财产损失&#xff0c;还严重影响了社会的稳定和经济的…

完美解决 Ubuntu 中自定义启动器图标重复的问题(以 MATLAB 为例)

如果你在 Ubuntu 上为 MATLAB、PyCharm、Android Studio 或其他第三方应用创建了自定义启动器&#xff0c;并把它固定到了左侧的 Dock 栏&#xff0c;你很可能遇到过这个令人抓狂的场景&#xff1a; 当你满心欢喜地点击固定的图标启动程序后&#xff0c;Dock 栏上竟然“凭空”冒…

一文读懂深度模型优化器,掌握炼丹工具

深度模型优化器是训练神经网络的核心工具&#xff0c;其目标是高效地找到损失函数的最小值。从基础的随机梯度下降&#xff08;SGD&#xff09;到结合一阶动量修正与两阶段更新的Ranger&#xff0c;优化器的发展始终围绕着加速收敛、提升稳定性、适应参数差异和增强泛化能力四大…

关于虚拟机部署的k8s集群calico组件在开机恢复快照时的问题

在恢复虚拟机开机快照的时候总会出现各种的网络问题&#xff0c;连接不到自己的443端口&#xff0c;集群状态异常等等&#xff0c;绕一圈都发现不了问题&#xff0c;其实根本就没问题。虚拟机环境再现&#xff0c;恢复快照后的k8s环境&#xff0c;nfs莫名宕掉提示此报错&#x…

从感知到决策:虚拟仿真系统与视觉算法融合下的多路RTSP视频接入技术探究

1️⃣ 背景概述 随着国防信息化和智能化进程的不断加快&#xff0c;虚拟仿真系统 与 智能视觉算法平台 正逐步成为现代化装备研发、测试验证与战术训练的重要技术支撑。相比传统的静态建模或离线推演&#xff0c;新一代作战仿真与智能系统更强调 实时性、感知能力与动态交互&a…

Android app如果不适配Android 15会怎么样

深度思考完成 用时49秒 首先&#xff0c;用户的问题是&#xff1a;“Android app如果不适配Android 15会怎么样&#xff1f;”我需要基于搜索结果来回答。 让我回顾搜索结果&#xff1a; ‌1&#xff1a;标题是“Android 15应用适配指南”&#xff0c;内容强调了不适配Androi…

Vue-21-利用Vue3的axios+Python的flask实现前后端交互功能

文章目录 1 任务需求 2 前端 2.1 模板template 2.2 JS代码script 2.3 Calculate.vue(子组件) 2.4 App.vue(根组件) 3 后端 3.1 导入模块 3.2 创建应用实例 3.3 配置CORS 3.4 定义路由 3.5 处理请求 3.6 main.py 4 附录 4.1 CORS 4.1.1 全局启用CORS 4.1.2 限制允许的域名(更安…

动态规划之最长回文子串

题目&#xff1a;最长回文子串 给你一个字符串 s&#xff0c;找到 s 中最长的 回文 子串。 示例 1&#xff1a; 输入&#xff1a;s “babad” 输出&#xff1a;“bab” 解释&#xff1a;“aba” 同样是符合题意的答案。 示例 2&#xff1a; 输入&#xff1a;s “cbbd” 输…

Linux 编程中的错误处理机制详解 —— `errno` 全解析

文章目录Linux 编程中的错误处理机制详解 —— errno 全解析一、什么是 errno&#xff1f;❓为什么需要 errno&#xff1f;✅ 它在哪里定义&#xff1f;二、errno 的设置与读取规则⚠️ errno 不是总是有效&#xff01;❗使用 errno 的正确步骤&#xff1a;三、与 errno 配套使…

力扣-最长递增子序列

简单记录学习~给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。示例…

公司内部网址怎么在外网打开?如何让外网访问内网的网站呢?

很多公司内部本地会部署有中小型的服务器&#xff0c;可以很好的方便用于一些办公业务系统&#xff0c;或测试开发需要。在数字化办公和生活场景中&#xff0c;除了公司内部局域网内访问公司系统外&#xff0c;经常会遇到需要让外网访问内网网站的情况。比如企业员工远程办公时…

有趣的css - 多选立体标签按钮

&#x1f36d; 大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是一个交互较完整的多选立体标签按钮。 最新文章通过公众号「设计师工作日常」发布。 目录整体效果核心代码html 代码css 部分代码完整代码如下html 页面css 样式页面渲…

C++中byte*和char*的区别

在C中&#xff0c;byte*&#xff08;通常指 std::byte*&#xff09;和 char* 都是指针类型&#xff0c;但它们在语义和用途上有重要区别&#xff1a;1. 类型定义char* char 是C内置的基本类型&#xff0c;表示字符&#xff08;通常是1字节&#xff09;。 char* 常用于&#xff…

【node】npm包本地开发与调试

npm link 进入本地的 babel-plugin-function-try-catch 这个 npm 包的根目录执行&#xff1a; npm link上面的命令可以将当前的这个包安装在全局&#xff08;mac 中的路径是 /usr/local/bin&#xff09;&#xff0c;也就是 npm i -g 安装包的目录。 执行后结果如图&#xff…

突破量子仿真瓶颈:微算法科技MLGO量子算法的算术化与核操作迭代模型

近年来&#xff0c;量子计算机的迅速发展和潜在的强大计算能力吸引了全球科研机构和企业的广泛关注。量子计算机利用量子力学的特性来处理复杂的计算任务&#xff0c;具有在某些方面远超经典计算机的潜力。然而&#xff0c;真正实用的量子计算机尚未大规模普及&#xff0c;因此…

python中读取 Excel 表格数据

在pandas中读取 Excel 表格后&#xff0c;有多种方式可以按列、按行提取数据&#xff0c;下面我将详细介绍常见的方法。 0.声明 在本文中我使用的excel表内容如下&#xff1a;1. 读取 Excel 文件 首先&#xff0c;我们需要使用 pandas 的 read_excel 函数读取 Excel 文件&#…

算法训练营day28 贪心算法②122.买卖股票的最佳时机II、55. 跳跃游戏、 45.跳跃游戏II 、1005.K次取反后最大化的数组和

贪心算法第二篇博客&#xff01;感觉这篇博客中的算法都很巧妙&#xff0c;需要动动脑筋 122.买卖股票的最佳时机II &#xff08;这道题可以遍历数组&#xff0c;如果不能遍历的话&#xff0c;就不能做了&#xff0c;需要注意的是&#xff1a; 只有一只股票&#xff01;当前只…

NumPy核心操作全攻略

NumPy&#xff08;Numerical Python&#xff09;是 Python 生态中用于科学计算的核心库&#xff0c;提供高性能的多维数组对象&#xff08;ndarray&#xff09;及相关的数学运算工具。其核心功能围绕数组操作、线性代数、随机数生成等&#xff0c;是数据科学、机器学习等领域的…

Redis 主从同步对象模型

淘汰策略 对最外层的key进行淘汰 expire(秒)/pexpire(毫秒) ttlmaxmemory:最大内存的一半(持久化fork()子进程) 数据迁移需要额外的空间 maxmemory-policy 提供淘汰机制 默认不会淘汰 lru 最近最少使用 lfu最近最少频次 voltaile 对由expire的进行淘汰持久化: fork:写时复制原理…

C++ 使用 constexpr 、查表法、分治法加速位镜像翻转

代码////// brief 左右翻转位。////// note 翻转后&#xff0c;最低位位将变为最高位&#xff0c;最高位将变为最低位。//////template <typename T>requires(std::is_same_v<T, uint8_t>)constexpr T Reverse(T value){int32_t bit_count sizeof(T) * 8;for (int…