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

多執行緒程式設計(四):執行緒同步機制全解析

     編輯:關於VC++

為何需要同步?

多執行緒共享進程資源(全域變數、檔案、裝置等),若無協調機制,會發生競爭條件:

  • 兩執行緒同時寫入磁碟 → 數據損壞
  • 一執行緒讀取資料庫時另一執行緒正更新中 → 讀到髒數據

MFC 提供四大同步物件,均衍生自 CSyncObject:

四大同步物件比較

類別適用場景跨進程效能
CCriticalSection單一進程內保護共享資源/代碼段否最高(核心模式切換少)
CMutex跨進程互斥存取是較低(核心物件開銷大)
CEvent執行緒間通知/等待特定條件是中等
CSemaphore限制同時存取資源的執行緒數量是中等

A. CCriticalSection:輕量級互斥鎖

同一時間僅允許一執行緒進入臨界區。

// 全域宣告(確保各執行緒可見)
CCriticalSection g_cs;
char g_Array[10];

UINT WriteW(LPVOID pParam) {
    CEdit* pEdit = (CEdit*)pParam;
    pEdit->SetWindowText(_T(""));
    g_cs.Lock(); // 獲得鎖,若被佔用則阻塞等待
    for (int i=0; i<10; i++) {
        g_Array[i] = 'W';
        pEdit->SetWindowText(g_Array);
        Sleep(1000);
    }
    g_cs.Unlock(); // 釋放鎖
    return 0;
}

// WriteD 同理,將 'W' 改為 'D'

關鍵行為:執行緒 A 執行到 Lock() 時,若執行緒 B 正在臨界區內(已 Lock 尚未 Unlock),A 會阻塞等待直到 B 呼叫 Unlock()。

B. CEvent:訊號通知機制

類似「旗標」,一執行緒設定訊號,另一執行緒等待訊號。

// 全域事件物件,預設自動重置、無訊號狀態
CEvent g_eventWriteD;
char g_Array[10];

UINT WriteW(LPVOID pParam) {
    CEdit* pEdit = (CEdit*)pParam;
    pEdit->SetWindowText(_T(""));
    for (int i=0; i<10; i++) {
        g_Array[i] = 'W';
        pEdit->SetWindowText(g_Array);
        Sleep(1000);
    }
    g_eventWriteD.SetEvent(); // 發送訊號,喚醒等待執行緒
    return 0;
}

UINT WriteD(LPVOID pParam) {
    CEdit* pEdit = (CEdit*)pParam;
    pEdit->SetWindowText(_T(""));
    // 等待訊號,INFINITE 表示無限期等待
    WaitForSingleObject(g_eventWriteD.m_hObject, INFINITE);
    for (int i=0; i<10; i++) {
        g_Array[i] = 'D';
        pEdit->SetWindowText(g_Array);
        Sleep(1000);
    }
    return 0;
}

自動 vs 手動事件:

  • 自動事件(預設):SetEvent() 釋放**一個**等待執行緒後自動重置為無訊號
  • 手動事件:SetEvent() 後保持有訊號,需呼叫 ResetEvent() 手動重置

C. CMutex:跨進程互斥鎖

用法與 CCriticalSection 類似(Lock()/Unlock()),但可跨進程使用。同進程內優先用 CCriticalSection 效能更好。

D. CSemaphore:資源計數器

控制同時存取資源的執行緒上限。建構時指定最大計數與初始可用計數。

// 最多 2 執行緒同時存取,初始可用 2
CSemaphore g_sem(2, 2);
char g_Array[10];

UINT WriteA(LPVOID pParam) {
    CEdit* pEdit = (CEdit*)pParam;
    pEdit->SetWindowText(_T(""));
    WaitForSingleObject(g_sem.m_hObject, INFINITE); // 計數 -1
    CString str;
    for (int i=0; i<10; i++) {
        pEdit->GetWindowText(str);
        g_Array[i] = 'A';
        str += g_Array[i];
        pEdit->SetWindowText(str);
        Sleep(1000);
    }
    ReleaseSemaphore(g_sem.m_hObject, 1, NULL); // 計數 +1
    return 0;
}
// WriteB, WriteC 同理

三個執行緒同時啟動時,只有兩個能進入迴圈,第三個會在 WaitForSingleObject 阻塞,直到前釋放信號量。

範例專案結構

  • MultiThread8:CCriticalSection 保護共用陣列
  • MultiThread9:CEvent 實現寫 W 完成後通知寫 D
  • MultiThread10:CSemaphore(2,2) 限制同時寫入執行緒數為 2

最佳實踐建議

  1. 同進程共享資源 → CCriticalSection(最輕量)
  2. 需跨進程同步 → CMutex 或具名 CEvent/CSemaphore
  3. 條件等待/通知 → CEvent
  4. 資源池/連線池限制併發數 → CSemaphore
  5. 務必成對呼叫 Lock/Unlock、Wait/Release,建議用 RAII 包裝避免洩漏

結語

掌握四大同步物件特性與適用場景,是編寫穩健多執行緒程式的基石。實務上常組合使用(如 Critical Section 保護佇列 + Event 通知有新任務),靈活運用才能事半功倍。

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