技術演進中的開發沉思-40 MFC系列:多線程協作

今天說說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…