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

MFC 線程通訊核心技術:事件物件的同步與訊號機制

     編輯:關於VC++

線程間通訊不僅限於資料共享,更包含執行順序協調、狀態變更通知等場景。事件物件是 Windows 核心同步物件中最適合「訊號-等待」模式的機制。本文詳細解析 MFC CEvent 類的手動/自動重置模式、跨線程訊號傳遞,並透過完整範例示範線程啟動控制與優雅終止。

事件物件的兩種核心模式

CEvent 建構函數原型:

CEvent(
    BOOL bInitiallyOwn = FALSE,  // 初始狀態:TRUE=有訊號, FALSE=無訊號
    BOOL bManualReset = FALSE,   // TRUE=手動重置, FALSE=自動重置
    LPCTSTR lpszName = NULL,     // 跨進程名稱
    LPSECURITY_ATTRIBUTES lpsaAttribute = NULL
);
模式行為特徵適用場景
自動重置單一等待線程被喚醒後自動重置為無訊號工作佇列、單一消費者模式
手動重置維持有訊號狀態直到明確呼叫 ResetEvent()廣播通知、多線程同步啟動

核心操作 API

  • SetEvent():設定為有訊號狀態,喚醒等待線程
  • ResetEvent():設定為無訊號狀態(手動重置模式必須)
  • PulseEvent():瞬間設定並重置(自動重置模式喚醒單一線程,手動重置喚醒所有)
  • WaitForSingleObject():等待事件進入有訊號狀態

實戰範例:線程生命週期完整控制

以下範例展示如何使用雙事件(啟動/終止)精確控制工作者線程:

// 全域事件物件
CEvent ThreadBegin;  // 自動重置,初始無訊號
CEvent ThreadEnd;    // 手動重置,初始無訊號

UINT ThreadProc(LPVOID param)
{
    // 1. 等待啟動訊號(無限期阻塞)
    ::WaitForSingleObject(ThreadBegin.m_hObject, INFINITE);
    ::MessageBox((HWND)param, "Thread Activated!", "Thread", MB_OK);

    BOOL KeepRunning = TRUE;
    while (KeepRunning)
    {
        // 執行週期性工作...
        
        // 2. 輪詢終止訊號(非阻塞檢查)
        int Result = ::WaitForSingleObject(ThreadEnd.m_hObject, 0);
        if (Result == WAIT_OBJECT_0)
            KeepRunning = FALSE;
    }
    
    ::MessageBox((HWND)param, "Thread Stopped!", "Thread", MB_OK);
    return 0;
}

// 主線程控制邏輯
HWND hWnd = GetSafeHwnd();
AfxBeginThread(ThreadProc, hWnd);  // 線程建立但阻塞於 ThreadBegin
ThreadBegin.SetEvent();            // 發送啟動訊號,線程開始執行

// ... 適當時機發送終止訊號 ...
ThreadEnd.SetEvent();              // 手動重置事件,線程下次輪詢即退出

進階技巧與注意事項

1. 事件與訊息佇列的協作

GUI 線程不可長時間阻塞於 WaitForSingleObject,否則導致介面凍結。解決方案:

// 使用 MsgWaitForMultipleObjects 處理訊息佇列同時等待事件
while (TRUE)
{
    DWORD result = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);
    if (result == WAIT_OBJECT_0) break;  // 事件觸發
    if (result == WAIT_OBJECT_0 + 1)     // 有訊息到達
        ProcessMessages();
}

2. 跨進程事件同步

建構時指定名稱即可實現跨進程訊號傳遞:

CEvent GlobalEvent(FALSE, TRUE, "Global\\MyApp_ShutdownEvent");
// 其他進程開啟同名事件
CEvent GlobalEvent(FALSE, TRUE, "Global\\MyApp_ShutdownEvent");

3. 避免競態條件

事件建立與線程啟動順序至關重要:

  1. 先建立事件物件(初始無訊號)
  2. 啟動工作者線程(進入等待狀態)
  3. 主線程完成初始化工作
  4. 呼叫 SetEvent() 釋放工作者線程

總結

事件物件是線程協調的瑞士刀,特別適合「等待條件滿足再執行」的場景。掌握手動/自動重置模式差異、善用輪詢與阻塞等待組合、注意 GUI 線程訊息處理,能構建出健壯且回應靈敏的多線程應用程式。

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