首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Win32 多線程程式設計實戰:串口通訊與網絡通訊綜合範例

     編輯:關於C++

前言

在工業控制與嵌入式系統開發中,多線程技術是實現高效並發處理的關鍵。本文將以最典型的串口通訊為切入點,詳細講解 Win32 平台下多線程串口程式的設計流程,並簡要介紹多線程在網絡通訊中的兩種主流架構模式。

一、Win32 串口通訊基礎

在 Windows 平台上,串口設備被視為一種特殊的文件,操作方式與普通文件讀寫高度一致,主要分為三個步驟:

  1. 打開通訊端口:使用 CreateFile 獲取設備句柄。
  2. 初始化串口參數:透過 GetCommState/SetCommState 配置 DCB 結構,設定波特率、資料位、停止位、奇偶校驗等。
  3. 讀寫資料:配合 SetCommMask、WaitCommEvent 實現事件驅動的非同步收發。

1.1 開啟與配置串口

CreateFile 的關鍵參數說明:

  • lpFileName:如 "COM1"
  • dwDesiredAccess:GENERIC_READ | GENERIC_WRITE
  • dwShareMode:串口獨佔,必須為 0
  • dwCreationDisposition:OPEN_EXISTING
  • dwFlagsAndAttributes:非同步 I/O 需指定 FILE_FLAG_OVERLAPPED

DCB 結構包含完整的串口參數,如 BaudRate、ByteSize、Parity、StopBits 等,配置前需先取得當前狀態再修改寫回。

1.2 事件驅動機制

Win32 提供豐富的通訊事件:

事件常數含義
EV_RXCHAR接收緩衝區有字元
EV_TXEMPTY發送緩衝區已空
EV_CTS / EV_DSR / EV_RLSDModem 控制訊號變化
EV_ERR線路狀態錯誤
EV_BREAK / EV_RING / EV_RXFLAG斷線、響鈴、特殊字元

使用 SetCommMask 啟用關注的事件,再呼叫 WaitCommEvent 等待事件發生,配合 ReadFile/WriteFile 完成資料傳輸。

二、多線程串口通訊工程實例

範例專案 MultiThreadCom 建立兩個工作者線程,分別處理 COM1 與 COM2,採用最簡化的三線制(僅 RX/TX)連接,故僅關注 EV_RXCHAR 與 EV_TXEMPTY 兩種事件。

2.1 應用程式初始化

在 CMultiThreadComApp::InitInstance() 中依序開啟並配置兩個串口,設定波特率 9600,清空發送緩衝區:

hComm1 = CreateFile("COM1", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
DCB wdcb; GetCommState(hComm1, &wdcb);
wdcb.BaudRate = 9600; SetCommState(hComm1, &wdcb);
PurgeComm(hComm1, PURGE_TXCLEAR);

2.2 工作者線程啟動

對話框 OnInitDialog 中以 CreateThread 建立兩條線程,傳入主視窗句柄以便線程通過 PostMessage 通知 UI:

hCommThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Com1ThreadProcess, AfxGetMainWnd()->m_hWnd, 0, &nThreadId1);

2.3 線程處理函數核心邏輯

每個線程進入無限迴圈,呼叫 WaitCommEvent 等待事件,根據事件類型執行收發:

DWORD WINAPI Com1ThreadProcess(HWND hWnd) {
    DWORD wEven; SetCommMask(hComm1, EV_RXCHAR | EV_TXEMPTY);
    while (TRUE) {
        WaitCommEvent(hComm1, &wEven, NULL);
        switch (wEven) {
            case EV_TXEMPTY:
                if (wTxPos < wTxLen) WriteFile(hComm1, com1Data.TxBuf[wTxPos++], 1, &wCount, NULL);
                break;
            case EV_RXCHAR:
                if (com1Data.wRxPos < com1Data.wRxLen) {
                    ReadFile(hComm1, com1Data.RxBuf[com1Data.wRxPos++], 1, &wCount, NULL);
                    if (com1Data.wRxPos == com1Data.wRxLen) ::PostMessage(hWnd, COM_SENDCHAR, 0, 1);
                }
                break;
        }
    }
    return TRUE;
}

資料結構 tagSerialPort 獨立管理每個串口的收發緩衝區與位置指標,實現線程間資料隔離。

三、封裝級多線程串口類 —— CSerialPort 深度解析

Remon Spekreijse 開源的 CSerialPort 類將上述底層 API 封裝為易用的 C++ 類,展示了專業級的多線程同步設計。

3.1 類架構設計

  • 線程模型:採用 CWinThread 衍生的工作者線程,通過 StartMonitoring/StopMonitoring/RestartMonitoring 控制生命週期。
  • 同步機制:CRITICAL_SECTION 保護通訊資源,三個事件組成陣列 m_hEventArray:關閉事件(最高優先級)、Overlapped I/O 事件、寫入觸發事件。
  • 非同步 I/O:建立通訊埠時指定 FILE_FLAG_OVERLAPPED,所有讀寫操作配合 OVERLAPPED 結構實現非阻塞。

3.2 核心線程函數 CommThread

雙重等待策略:

  1. WaitCommEvent 監聽硬體事件(接收字元、CTS 變化等)。
  2. WaitForMultipleObjects 監聽使用者控制事件(關閉、寫入請求)。

收到 EV_RXCHAR 時呼叫 ReceiveChar 循環讀取所有可用字元,並以 SendMessage 逐字元通知擁有者視窗;收到寫入事件則呼叫 WriteChar 發送緩衝區資料。

3.3 關鍵實現細節

  • 錯誤處理:ProcessErrorMessage 利用 FormatMessage 取得系統錯誤描述,彈出對話框提示。
  • 緩衝區管理:動態分配寫入緩衝區,讀取採單字元循環,確保不遺漏任何資料。
  • 執行緒安全關閉:解構函數中發送關閉事件並等待 m_bThreadAlive 變為 FALSE,確保執行緒正常退出後再釋放資源。

四、多線程網絡通訊架構模式

網絡伺服器開發中,多線程應用主要有兩種經典模式:

4.1 主監控線程模式(動態生成,靜態調度)

  • 主線程 listen/accept,每新增一個連線即 CreateThread 建立子線程專責處理。
  • 優點:原型開發快,適合連線數少、存活時間長的場景。
  • 缺點:頻繁建立/銷毀線程開銷大,主線程對子線程控制力弱。

4.2 線程池模式(靜態生成,動態調度)

  • 啟動時預先建立固定數量的懸掛線程放入池中。
  • 新連線到來時,主線程從池中取出閒置線程分派任務;任務結束後線程歸池等待復用。
  • 優點:主線程可精細調度,對客戶端監控能力強,適合高頻短連線場景。
  • 缺點:調度邏輯增加複雜度,但整體資源消耗遠低於動態建立線程。

結語

從串口通訊的底層 API 到封裝類的設計藝術,再到網絡伺服器的架構抉擇,多線程程式設計的核心始終圍繞著「資源隔離、同步協作、生命週期管理」三大主題。掌握 Win32 串口多線程實戰,不僅能應對工控嵌入式開發,更能為理解高併發網絡編程奠定堅實基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved