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

Windows 多線程多任務設計入門:線程管理、同步與通信全解析

     編輯:VC 教學

前言:為什麼必須掌握多線程

現代 Windows 同時運行多進程,單進程內又可劃分多線程(獨立執行流)。多線程提供多任務處理能力,是當代軟件開發的基石。單線程軟件已不可想象。本文針對線程間通信、同步等高頻問題展開探討。

一、理解線程

進程 vs 線程

  • 進程: 應用程序執行實例,擁有私有虛擬地址空間、代碼、數據、系統資源。進程結束,資源隨之釋放。
  • 線程: 進程內部獨立執行單元,對應 CWinThread 對象。單進程至少有一主線程(main/WinMain 入口),主線程結束 → 進程結束。可按需創建多線程並行運行。

線程特性

  • 同進程所有線程共享虛擬地址空間、全局變量、系統資源。
  • OS 分配 CPU 時間片輪轉執行,宏觀並行,微觀串行。
  • 優先級高的線程優先獲得 CPU,優先權層次不同則高層次優先。

兩類線程

類型用途創建方式
用戶界面線程處理用戶輸入、響應消息/事件從 CWinThread 派生,用 DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE,調用 AfxBeginThread 重載版本啟動
工作線程後台計算、調度、串口讀寫等無需派生類,編寫控制函數,直接調用 AfxBeginThread(pfnThreadProc, param, priority)

注意: 所有線程共享父進程變量,但每個線程可擁有自己的局部變量(棧上)。

二、線程管理與操作

1. 線程啟動

用戶界面線程

CWinThread* AfxBeginThread(
    CRuntimeClass* pThreadClass, 
    int nPriority = THREAD_PRIORITY_NORMAL,
    UINT nStackSize = 0,
    DWORD dwCreateFlags = 0, // CREATE_SUSPENDED 掛起啟動
    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

工作線程

UINT ThreadFunc(LPVOID pParam) { /* 工作邏輯 */ return 0; }
CWinThread* pThread = AfxBeginThread(ThreadFunc, param, priority);

2. 優先級操作

int GetThreadPriority();
BOOL SetThreadPriority(int nPriority); // 相對於所在優先權層次
// 優先權層次需用 Win32 API: GetPriorityClass/SetPriorityClass

設置優先級需線程創建時具備 THREAD_SET_INFORMATION 權限。

3. 掛起與恢復

SuspendThread(); // 暫停
ResumeThread();  // 恢復
// 多次 Suspend 需同次數 Resume 才能恢復

4. 線程終止三種方式

  1. 線程內部調用 AfxEndThread() 自殺。
  2. 外部強制終止:TerminateThread(hThread, dwExitCode) + CloseHandle() 釋放棧(不推薦,易導致資源洩漏)。
  3. 推薦: 改變全局標誌變量,讓線程函數自然返回結束。

範例:標誌變量優雅退出

BOOL bEnd = FALSE; // 全局標誌
UINT ThreadFunction(LPVOID pParam) {
    while (!bEnd) { Beep(100,100); Sleep(1000); }
    return 0;
}

void CTestView::OnInitialUpdate() {
    hWnd = GetSafeHwnd();
    pThread = AfxBeginThread(ThreadFunction, hWnd);
    pThread->m_bAutoDelete = FALSE; // 手動刪除
}

void CTestView::OnDestroy() {
    bEnd = TRUE; // 設置標誌
    WaitForSingleObject(pThread->m_hThread, INFINITE); // 等待結束
    delete pThread; // 釋放對象
    CView::OnDestroy();
}

三、線程間通信

次級線程為主線程完成任務,需建立通信通道。主要方法:全局變量、事件對象、消息。

1. 用戶定義消息通信

每線程擁有消息隊列(工作線程亦然)。

#define WM_USERMSG (WM_USER + 100)
// 發送
::PostMessage(hWnd, WM_USERMSG, 0, 0);
// 或
CWinThread::PostThreadMessage();

示例:工作線程結束通知主線程

UINT ThreadFunction(LPVOID pParam) {
    while (!bEnd) { Beep(100,100); Sleep(1000); }
    ::PostMessage(hWnd, WM_USERMSG, 0, 0); // 通知主窗口
    return 0;
}

LONG CTestView::OnThreadEnded(WPARAM wParam, LPARAM lParam) {
    AfxMessageBox(_T("Thread ended."));
    return 0;
}

若工作線程也需接收消息,可在控制函數中用 ::GetMessage() 實現消息循環,隊列空時自動讓出 CPU。

2. 事件對象通信

用 CEvent 對象,兩態:有信號/無信號。線程等待有信號事件執行操作。

CEvent threadStart, threadEnd;

UINT ThreadFunction(LPVOID pParam) {
    ::WaitForSingleObject(threadStart.m_hObject, INFINITE); // 等待啟動信號
    AfxMessageBox(_T("Thread start."));
    while (!bEnd) {
        Beep(100,100); Sleep(1000);
        int result = ::WaitForSingleObject(threadEnd.m_hObject, 0); // 非阻塞檢查
        if (result == WAIT_OBJECT_0) bEnd = TRUE;
    }
    ::PostMessage(hWnd, WM_USERMSG, 0, 0);
    return 0;
}

void CTestView::OnInitialUpdate() {
    hWnd = GetSafeHwnd();
    threadStart.SetEvent(); // 發送啟動信號
    pThread = AfxBeginThread(ThreadFunction, hWnd);
    pThread->m_bAutoDelete = FALSE;
}

void CTestView::OnDestroy() {
    threadEnd.SetEvent(); // 發送結束信號
    WaitForSingleObject(pThread->m_hThread, INFINITE);
    delete pThread;
}

運行效果:關閉程序時才彈出「Thread ended」,證明線程正常同步結束。

四、線程同步:保護共享數據

多線程訪問共享變量需同步,防止數據競爭。MFC 提供 7 個同步類,分兩類:

  • 同步對象: CSyncObject、CSemaphore、CMutex、CCriticalSection、CEvent
  • 同步訪問對象: CMultiLock、CSingleLock

1. 臨界區

同一時刻僅允許一線程訪問受保護數據。共享 CCriticalSection 對象,訪問前 Lock(),結束 Unlock()。

#include "afxmt.h"
int array[10], destarray[10];
CCriticalSection Section;

UINT WriteThread(LPVOID) {
    Section.Lock();
    for (int x=0; x<10; x++) array[x] = x;
    Section.Unlock();
    return 0;
}

UINT ReadThread(LPVOID) {
    Section.Lock();
    for (int x=0; x<10; x++) destarray[x] = array[x];
    Section.Unlock();
    return 0;
}

結果:destarray 正確複製 0-9。無同步則數據亂序。

2. 互斥量

功能類似臨界區,但可跨進程同步。需配合 CSingleLock/CMultiLock 使用。

CMutex Section;

UINT WriteThread(LPVOID) {
    CSingleLock lock(&Section);
    lock.Lock();
    for (int x=0; x<10; x++) array[x] = x;
    lock.Unlock();
    return 0;
}
// ReadThread 同理

3. 信號量

允許多線程同時訪問(計數器機制)。創建 CSemaphore(初始計數, 最大計數),Lock() 遞減,Unlock() 遞增。


CSemaphore* semaphore = new CSemaphore(2, 2); // 允許2線程同時訪問
HWND hWnd = GetSafeHwnd();
AfxBeginThread(ThreadProc1, hWnd);
AfxBeginThread(ThreadProc2, hWnd);
AfxBeginThread(ThreadProc3, hWnd);

UINT ThreadProc1(LPVOID param) {
    CSingleLock lock(semaphore);
    lock.Lock();
    Sleep(10000);
    ::MessageBox((HWND)param, _T("Thread1 had Access"), _T("Thread1"), MB_OK);
    return 0;
}
// ThreadProc2/3 相同

效果:前兩線程立即彈窗,第三線程等待 10 秒後才獲取信號量彈窗。

總結

多線程賦予應用高效、快速、安全的數據處理能力。掌握線程創建、優先級控制、優雅退出、消息/事件通信、臨界區/互斥/信號量同步,是構建健壯 Windows 應用的核心技能。

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