技術演進中的開發沉思-9:window編程系列-內核對象線程同步(下)

今天我們繼續走進 Windows 內核的世界,就昨天沒說完的內核對象與線程同步內容接著繼續,它們就像精密儀器里的齒輪,雖不顯眼,卻至關重要。

異步設備 I/O

在 Windows 系統中,異步設備 I/O 就像是一場精心編排的接力賽。想象一下,我們的計算機系統是一個龐大的工廠,各個設備(比如硬盤、網卡)就是工廠里忙碌的工人,而應用程序則是負責下訂單的客戶。當應用程序需要從硬盤讀取數據時,如果采用同步 I/O,就好比客戶站在工廠門口,眼巴巴地等著工人把貨物一件件搬出來,在這個過程中,客戶什么都做不了,只能干等。而異步 I/O 則不同,它允許客戶下完訂單后,不用傻等,繼續去做其他事情,工廠(設備)在準備好貨物后,會主動通知客戶來取。

在 Windows 編程中,使用重疊 I/O(一種異步 I/O 方式)來實現這個過程。下面是一段簡單的 VC++ 代碼示例,展示如何使用異步 I/O 從文件中讀取數據:


#include <windows.h>#include <stdio.h>int main() {HANDLE hFile = CreateFile(TEXT("test.txt"),GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);if (hFile == INVALID_HANDLE_VALUE) {printf("Failed to open file. Error: %d\n", GetLastError());return 1;}OVERLAPPED overlapped = { 0 };overlapped.Offset = 0;overlapped.OffsetHigh = 0;overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);DWORD bytesRead;if (!ReadFile(hFile, NULL, 0, &bytesRead, &overlapped)) {if (GetLastError() != ERROR_IO_PENDING) {printf("ReadFile failed. Error: %d\n", GetLastError());CloseHandle(hFile);CloseHandle(overlapped.hEvent);return 1;}}// 可以在等待數據讀取完成的過程中做其他事情// 當數據讀取完成,事件會被觸發WaitForSingleObject(overlapped.hEvent, INFINITE);CloseHandle(hFile);CloseHandle(overlapped.hEvent);return 0;}

在這段代碼里,CreateFile函數打開文件時設置了FILE_FLAG_OVERLAPPED標志,開啟異步模式。ReadFile函數在數據未準備好時立即返回,我們通過等待overlapped.hEvent事件來得知數據是否讀取完成。這樣,程序就不會在讀取數據時卡住,而是可以高效地利用時間,處理其他任務,就像接力賽中,下一棒選手可以提前做好準備,而不是傻傻地站在原地等待。

二、WaitForInputIdle 函數

WaitForInputIdle函數就像是一位耐心的管家。在 Windows 系統中,當我們啟動一個新的進程,比如打開一個應用程序時,這個程序可能需要一些時間來初始化,加載資源、設置窗口布局等等。在這個過程中,如果我們立即對它進行操作,可能會出現混亂,就好比一個剛起床還沒收拾好的人,你馬上讓他去接待客人,肯定會手忙腳亂。

WaitForInputIdle函數的作用就是讓我們等待程序完成初始化,準備好接收用戶輸入后,再進行后續操作。它就像管家在門口守著,告訴我們:“先別著急進去打擾,等里面準備好了,我再通知你。” 以下是一個簡單的使用示例:


#include <windows.h>#include <stdio.h>int main() {SHELLEXECUTEINFO ShExecInfo = { 0 };ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;ShExecInfo.hwnd = NULL;ShExecInfo.lpVerb = NULL;ShExecInfo.lpFile = TEXT("notepad.exe");ShExecInfo.lpParameters = NULL;ShExecInfo.lpDirectory = NULL;ShExecInfo.nShow = SW_NORMAL;ShExecInfo.hInstApp = NULL;if (ShellExecuteEx(&ShExecInfo)) {// 等待記事本程序準備好接收用戶輸入WaitForInputIdle(ShExecInfo.hProcess, INFINITE);printf("Notepad is ready for input.\n");// 可以在這里添加對記事本的操作代碼CloseHandle(ShExecInfo.hProcess);} else {printf("Failed to launch Notepad. Error: %d\n", GetLastError());}return 0;}

在這段代碼中,我們使用ShellExecuteEx函數啟動記事本程序,然后調用WaitForInputIdle函數等待記事本完成初始化。只有當記事本準備就緒,程序才會繼續執行后續操作,這就避免了因過早操作而可能引發的問題,讓整個過程更加順暢、有序。

三、MsgWaitForMultipleObjects(ex)函數

MsgWaitForMultipleObjects(ex)函數就像是一個忙碌的交通指揮員,它負責管理多個內核對象和消息隊列。在 Windows 系統中,我們的程序可能會創建多個線程,每個線程可能有自己的任務,同時,程序還需要處理各種消息(比如用戶的鼠標點擊、鍵盤輸入)。這些線程和消息就像道路上川流不息的車輛,如果沒有一個好的指揮,很容易造成混亂和擁堵。

MsgWaitForMultipleObjects(ex)函數可以同時等待多個內核對象(比如事件、信號量)變為有信號狀態,并且在等待過程中,還能處理消息隊列中的消息。它會根據不同的情況,決定是繼續等待內核對象,還是先處理消息,就像交通指揮員根據道路情況,靈活地指揮車輛通行,保證整個系統的流暢運行。下面是一個簡單的示例代碼:


#include <windows.h>#include <stdio.h>DWORD WINAPI ThreadProc(LPVOID lpParameter) {// 模擬線程執行任務for (int i = 0; i < 5; ++i) {printf("Thread is working...\n");Sleep(1000);}// 線程完成任務后設置事件HANDLE hEvent = (HANDLE)lpParameter;SetEvent(hEvent);return 0;}int main() {HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)hEvent, 0, NULL);DWORD result = MsgWaitForMultipleObjects(1,&hEvent,FALSE,INFINITE,QS_ALLINPUT);if (result == WAIT_OBJECT_0) {printf("Thread completed its task.\n");} else {printf("Error occurred. Result: %d\n", result);}CloseHandle(hThread);CloseHandle(hEvent);return 0;}

在這個示例中,我們創建了一個線程和一個事件。MsgWaitForMultipleObjects函數等待事件hEvent變為有信號狀態,同時在等待過程中,它還會處理消息隊列中的消息。當線程完成任務并設置事件后,MsgWaitForMultipleObjects函數就會返回,程序繼續執行后續操作,整個過程有條不紊,就像交通指揮員讓車輛順利通過繁忙的路口。

四、WaitForDebugEvent 函數

WaitForDebugEvent函數就像一位嚴謹的質檢員,專門負責監控和調試程序的運行狀態。在軟件開發過程中,程序難免會出現各種問題,就像生產線上的產品可能會有瑕疵。WaitForDebugEvent函數可以幫助我們捕獲程序運行時的各種事件(比如斷點命中、異常拋出),就像質檢員仔細檢查每一個產品,不放過任何一個可能存在的問題。

當我們使用調試器調試程序時,WaitForDebugEvent函數會等待調試事件的發生。一旦有調試事件出現,它就會通知調試器進行相應的處理,比如暫停程序執行、查看變量值等。以下是一個簡單的調試示例代碼框架:


#include <windows.h>#include <stdio.h>int main() {STARTUPINFO si = { sizeof(si) };PROCESS_INFORMATION pi;if (!CreateProcess(NULL,TEXT("test.exe"),NULL,NULL,FALSE,DEBUG_PROCESS,NULL,NULL,&si,&pi)) {printf("Failed to create process. Error: %d\n", GetLastError());return 1;}DEBUG_EVENT debugEvent;while (WaitForDebugEvent(&debugEvent, INFINITE)) {// 處理調試事件switch (debugEvent.dwDebugEventCode) {case EXCEPTION_DEBUG_EVENT:// 處理異常事件break;case CREATE_PROCESS_DEBUG_EVENT:// 處理進程創建事件break;// 其他類型的調試事件處理default:break;}ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);}CloseHandle(pi.hProcess);CloseHandle(pi.hThread);return 0;}

在這段代碼中,我們使用CreateProcess函數以調試模式啟動一個程序(這里假設為test.exe),然后通過WaitForDebugEvent函數循環等待調試事件的發生。一旦捕獲到調試事件,就根據事件類型進行相應的處理,處理完后使用ContinueDebugEvent函數讓程序繼續執行。這就像質檢員發現產品問題后,進行記錄和處理,確保產品質量符合要求,幫助開發者找到并解決程序中的問題。

五、SignalObjectAndWait 函數

SignalObjectAndWait函數就像一位默契的橋梁搭建者,它在兩個內核對象之間建立起一種特殊的聯系,實現原子操作。想象一下,有兩個任務,一個任務完成后需要通知另一個任務開始執行,同時還要確保在通知的過程中,不會出現其他干擾,保證整個過程的原子性(即要么都完成,要么都不完成)。

SignalObjectAndWait函數可以先將一個內核對象(比如事件)設置為有信號狀態,然后立即等待另一個內核對象變為有信號狀態。在這個過程中,它會確保設置信號和等待信號這兩個操作是連續進行的,不會被其他線程打斷。以下是一個示例代碼:


#include <windows.h>#include <stdio.h>int main() {HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);// 啟動一個線程,等待hEvent1變為有信號狀態HANDLE hThread = CreateThread(NULL, 0, [](LPVOID lpParameter) -> DWORD {WaitForSingleObject((HANDLE)lpParameter, INFINITE);printf("Thread received signal and started working.\n");// 線程完成任務后設置hEvent2為有信號狀態SetEvent((HANDLE)((DWORD_PTR)lpParameter + 1));return 0;}, (LPVOID)hEvent1, 0, NULL);// 主線程等待一段時間后,使用SignalObjectAndWait函數Sleep(2000);SignalObjectAndWait(hEvent1, hEvent2, INFINITE, FALSE);printf("Main thread completed the operation.\n");CloseHandle(hThread);CloseHandle(hEvent1);CloseHandle(hEvent2);return 0;}

在這個示例中,主線程使用SignalObjectAndWait函數先將hEvent1設置為有信號狀態,通知線程開始執行任務,然后等待hEvent2變為有信號狀態,即等待線程完成任務。整個過程通過SignalObjectAndWait函數實現了任務之間的有序協作,就像橋梁搭建者在兩個地點之間建起一座穩固的橋梁,讓任務的傳遞和執行更加順暢、可靠。

六、使用等待鏈遍歷 API 來檢測死鎖

在多線程編程中,死鎖是一個令人頭疼的問題,就像道路上車輛相互卡住,誰也動不了,導致整個系統陷入僵局。而使用等待鏈遍歷 API 來檢測死鎖,就像一位敏銳的故障偵探,能夠及時發現這些潛在的問題。

死鎖通常發生在多個線程互相等待對方釋放資源的情況下。等待鏈遍歷 API 可以幫助我們檢查線程之間的等待關系,通過分析等待鏈,找出是否存在循環等待的情況,從而判斷是否發生了死鎖。下面是一個簡單的死鎖檢測示例代碼框架(實際應用中會更復雜):


#include <windows.h>#include <stdio.h>// 模擬兩個線程競爭資源可能導致死鎖的情況DWORD WINAPI Thread1Proc(LPVOID lpParameter) {HANDLE hMutex1 = (HANDLE)((DWORD_PTR)lpParameter);HANDLE hMutex2 = (HANDLE)((DWORD_PTR)lpParameter + 1);WaitForSingleObject(hMutex1, INFINITE);Sleep(1000);WaitForSingleObject(hMutex2, INFINITE);ReleaseMutex(hMutex2);ReleaseMutex(hMutex1);return 0;}DWORD WINAPI Thread2Proc(LPVOID lpParameter) {HANDLE hMutex1 = (HANDLE)((DWORD_PTR)lpParameter);HANDLE hMutex2 = (HANDLE)((DWORD_PTR)lpParameter + 1);WaitForSingleObject(hMutex2, INFINITE);Sleep(1000);WaitForSingleObject(hMutex1, INFINITE);ReleaseMutex(hMutex1);ReleaseMutex(hMutex2);return 0;}int main() {HANDLE hMutex1 = CreateMutex(NULL, FALSE, NULL);HANDLE hMutex2 = CreateMutex(NULL, FALSE, NULL);HANDLE hThread1 = CreateThread(NULL, 0, Thread1Proc, (LPVOID)hMutex1, 0, NULL);HANDLE hThread2 = CreateThread(NULL, 0, Thread2Proc, (LPVOID)hMutex1, 0, NULL);// 模擬等待一段時間后進行死鎖檢測Sleep(3000);// 這里可以使用等待鏈遍歷API進行死鎖檢測,實際代碼會更復雜// 為簡化說明,暫不展開具體檢測代碼CloseHandle(hThread1);CloseHandle(hThread2);CloseHandle(hMutex1);CloseHandle(hMutex2);return 0;}

在這個示例中,兩個線程Thread1Proc和Thread2Proc以不同的順序獲取互斥鎖hMutex1和hMutex2,很可能會導致死鎖。在實際應用中,我們可以使用等待鏈遍歷 API 來檢測線程之間的等待關系,一旦發現存在循環等待的情況,就可以判斷發生了死鎖,并及時采取措施進行處理,就像偵探發現案件線索后,迅速展開調查并解決問題,保證系統的正常運行。

最后小結:

在我眼里,異步設備 I/O 如接力賽,通過重疊 I/O 實現異步讀取,讓程序在等待數據時能處理其他任務;WaitForInputIdle函數像耐心管家,確保新啟動程序完成初始化后再接收操作,避免混亂;MsgWaitForMultipleObjects(ex)函數是忙碌的交通指揮員,兼顧多個內核對象與消息隊列,維持系統運行秩序 。?

WaitForDebugEvent函數如同嚴謹質檢員,在程序調試時捕獲各類事件,助力開發者定位問題;SignalObjectAndWait函數是默契的橋梁搭建者,實現內核對象間原子操作,保障任務有序協作;等待鏈遍歷 API 則像敏銳的故障偵探,通過分析線程等待關系檢測死鎖,保障系統穩定。今天的內容就到這里吧!下一節,我們將梳理一下windows中很重要I/O相關的問題,未完待續.........

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/910155.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/910155.shtml
英文地址,請注明出處:http://en.pswp.cn/news/910155.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用AI從0開始量化交易-Anaconda環境(env)和緩存(pkg)更改儲存位置

之前介紹了Anaconda的安裝和環境建立&#xff0c;最近自己的量化交易工具開發的差不多了&#xff0c;卻發生了尷尬的問題&#xff0c;C盤被不斷增大的conda環境和緩存占據得快滿了。 在網上找了些教程&#xff0c;大多是講遷移的&#xff0c;專門講改本地改儲存位置的比較少&am…

Python爬蟲工作基本流程及urllib模塊詳解

在2025年的數據驅動時代&#xff0c;網絡數據成為企業與個人的“金礦”&#xff0c;而Python爬蟲則是挖掘這金礦的“利器”&#xff01;無論是抓取電商價格、分析社交媒體趨勢&#xff0c;還是構建知識庫&#xff0c;Python爬蟲都能讓你事半功倍。然而&#xff0c;爬蟲開發并非…

thinkphp8 模型-一對一,一對多,多對多 學習

thinkphp 命令創建模型&#xff08;和laravel基本一樣&#xff09; php think make:model User 在模型里創建字段 protected $table User; protected $pk id; // 定義返回哪些字段 protected $field [id, name]; // 返回字段的類型 protected $schema [id > int] 模…

非線性方程組求解:復雜情況下的數值方法

在科學研究和工程應用中&#xff0c;非線性方程組的求解是一個常見的挑戰。尤其當方程組包含復雜函數&#xff08;如特殊函數、積分、微分等&#xff09;&#xff0c;使得雅可比矩陣難以解析求導時&#xff0c;傳統的基于解析雅可比矩陣的 Newton-Raphson 方法難以直接應用。本…

邊緣計算網關EG8200Mini首發開箱視頻丨破解工業互聯“協議孤島”,重塑數據價值核心引擎行業痛點直擊|低代碼開發

數據采集4G邊緣計算網關plc 工業現場設備品牌林立&#xff08;西門子、三菱、歐姆龍等30品牌PLC&#xff09;、協議碎片化&#xff08;Modbus/OPC UA/BACnet等&#xff09;、網絡環境復雜&#xff08;戶外無光纖、車間電磁干擾&#xff09;——傳統網關難以實現多源異構設備統一…

2024-2025下期《網絡設備與配置》期末模擬測試

一、 單選題(每題2分&#xff0c;共60分) RIP協議的默認最大跳數是&#xff08; &#xff09; A. 10 B. 15 C. 20 D. 30以下哪個命令可以用來在交換機上進入全局配置模式&#xff1f;&#xff08; &#xff09; A. 使用enable命令 B. 使用configure terminal命令 C. 使用inte…

虹科案例 | 欣旺達如何實現動力電池測試的長期穩定性+自動化?

新能源汽車產業狂飆突進&#xff0c;動力電池測試正面臨前所未有的技術大考。 傳統電池測試方案常因數據丟幀、協議適配等問題&#xff0c;導致測試周期延長和交付延期。在這場關乎安全與效率的產業競速中&#xff0c;高精度數據采集與全球化交付能力&#xff0c;已成為動力電…

第17天:數據庫學習筆記1

數據庫學習筆記 1 SQL語言介紹 2 數據庫的安裝 2.1 啟動數據庫 方式一&#xff1a;net start mysql 方式二&#xff1a;在計算機管理里面手動打開數據庫 2.2 登錄MySQL 方式一&#xff1a;本地登錄 即數據庫與客戶端在同一臺電腦上。 方式二&#xff1a;遠程登錄 mysq…

ChromaDB完全指南:從核心原理到RAG實戰

一、引言:擁抱AI時代的“記憶”變革 在人工智能(AI)浪潮席卷全球的今天,大型語言模型(LLM)以其強大的自然語言處理能力,正在重塑我們與信息的交互方式。然而,LLM并非萬能,它們普遍存在知識截止日期、無法訪問私有數據等“記憶”短板。為了突破這一瓶頸,向量數據庫應…

XCUITest + Swift 詳細示例

??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】 【測試經驗】 【人工智能】 【Python】

Spring Boot + MyBatis + Redis Vue3 Docker + Kubernetes + Nginx

前言 前些天發現了一個巨牛的人工智能免費學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到網站 1.1 畢設項目需求分析&#xff08;附需求文檔片段&#xff09; 一、項目全景與技術選型 1.1 畢設項目需求分析&#xff08;附需…

【云計算領域數學基礎】組合數學優化

一、組合數學優化 1.1、定義與本質特征 1.1.1、組合數學優化的核心原理 ?問題本質與數學工具? ?組合爆炸問題?&#xff1a;軟件輸入參數、路徑組合隨規模指數級增長&#xff0c;如10個二值參數需1024個用例。組合數學通過覆蓋數組&#xff08;Covering Array&#xff09;、…

企業文檔如何變身AI語料庫?無憂文檔NLP+OCR技術實戰解析

當企業爭相采購ChatGPT、文心一言等通用大模型時&#xff0c;卻忽略了&#xff1a;企業文檔其實是這座數字油田的核心資產。從產品手冊、客戶案例到會議紀要&#xff0c;企業沉淀的海量文檔&#xff0c;這些看似零散的信息&#xff0c;其實正通過AI技術被轉化為可復用的“語料庫…

掌握Python編程的核心能力,能快速讀懂并上手項目開發。

掌握Python編程的核心能力&#xff0c;能快速讀懂并上手項目開發。 一套系統且通俗的講解&#xff0c;理論講解 實戰技巧 代碼框架模板&#xff0c;讓你能&#xff1a; 看懂Python項目結構 能自己寫代碼&#xff1a;函數、流程控制、類和模塊 能寫出一個完整、規范的Pytho…

「Linux文件及目錄管理」硬鏈接與軟連接

知識點解析 在Linux系統中,硬鏈接(Hard Link)和軟鏈接(Symbolic Link,又稱軟連接)是兩種不同的文件鏈接方式: 1.硬鏈接(Hard Link): 本質:硬鏈接是文件的一個別名,與原文件共享相同的inode和磁盤數據塊。特點: 數據共享:硬鏈接與原文件指向同一數據塊,修改任…

分清display三個屬性

display 三兄弟行為對比表格 屬性值是否換行能否設置寬高默認寬度常用標簽典型用途block是可以撐滿父容器<div>, <p>, <section>頁面結構、布局容器inline否不行隨內容大小<span>, <a>文字中嵌套、小圖標inline-block否可以隨內容大小<img&g…

《棒球青訓》打造幾個國家級運動基地·棒球1號位

Youth Baseball/Softball Base Development Plan | 青少年棒壘球基地建設方案 Core Strategies | 核心戰略 Regional Hub Construction | 區域樞紐建設 優先在 長三角/珠三角/成渝經濟圈 建設 3大示范性基地 每個基地包含&#xff1a; ?? 國際標準青少年賽場&#xff08;…

JavaScript Symbol 屬性詳解

一、Symbol 的本質與基礎 1. Symbol 是什么 JavaScript 的第七種原始數據類型&#xff08;ES6 引入&#xff09;創建唯一的、不可變的標識符主要用途&#xff1a;作為對象的屬性鍵&#xff08;Symbol 屬性&#xff09; // 創建 Symbol const id Symbol(id); // id 是描述符…

使用 INFINI Console 全面管理 Elasticsearch 9.X

1、引言 在搜索和分析領域&#xff0c;保持與最新版本的 Elasticsearch 同步對于利用新功能、提升性能和增強安全性至關重要。 Elasticsearch 9.X 作為 Elastic Stack 的最新版本&#xff0c;引入了多項改進&#xff0c;例如更高效的二進制量化和對 ColPali、ColBERT 等模型的支…

開疆智能ModbusTCP轉EtherCAT網關連接IVO編碼器配置案例

本案例是使用ModbusPOLL軟件通過開疆智能ModbusTCP轉EtherCAT網關連接編碼器的配置案例。具體操作步驟如下 配置過程 打開網關配置軟件“EtherCAT Manager”并新建項目選擇TCP2ECAT 設置網關的ModbusTCP一側的IP地址&#xff0c;要與主站軟件的組態配置保持一致。 添加松下伺服…