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

自動化線程管理器:CThreadManager + CThreadTask 實現線程生命週期自動回收

     編輯:關於VC++

前言

多線程編程中,手動追蹤 HANDLE、等待結束、CloseHandle 易疏漏導致資源洩漏。本文設計 「線程自管」 架構:CThreadManager 負責建立業務線程並登記至監控器,CThreadTask 內含專用監控線程定期掃描已結束線程並自動釋放句柄。使用者僅需提供線程函式與參數,其餘全自動。

架構圖解

線程自動管理流程圖

  1. 使用者建立 CThreadManager(threadFunc, param) → 內部 CreateThread 建立業務線程。
  2. CThreadManager 建構時自動啟動單例 CThreadTask 監控器 (若未啟動)。
  3. 業務線程 HANDLE 登記至 CThreadTask::m_ObList。
  4. 監控線程 (ThreadTaskProc) 每 500ms 喚醒,遍歷列表呼叫 GetExitCodeThread,發現 STILL_ACTIVE != exitCode 即 CloseHandle 並從列表移除。
  5. 使用者呼叫 RunThread() 開始執行 (對應 ResumeThread)。

核心類別定義

線程函式指標型別

typedef DWORD (WINAPI *ThreadProc)(LPVOID);

管理器類 CThreadManager

class CThreadManager : public CObject
{
    DECLARE_DYNAMIC(CThreadManager)
public:
    CThreadManager(ThreadProc proc, LPVOID param);
    ~CThreadManager();
    BOOL RunThread();                    // 恢復線程執行
    HANDLE GetThreadHandle() const { return m_Handle; }
private:
    HANDLE m_Handle;                     // 業務線程句柄
};

監控器類 CThreadTask (單例)

class CThreadTask : public CObject
{
    DECLARE_DYNAMIC(CThreadTask)
public:
    CThreadTask(); ~CThreadTask();
    BOOL IsValid();                      // 檢查監控線程是否已啟動
    void AddHandle(CONST HANDLE h);      // 登記業務線程句柄
    void CloseThreadHandles();           // 掃描並清理已結束線程
    static CThreadTask& GetThreadTask(); // 單例存取點

    CObList m_ObList;                    // 儲存 CHandle* 列表
    HANDLE  m_Handle;                    // 監控線程自身句柄
    BOOL    m_bKeepGoing;                // 監控迴圈旗標
};

句柄包裝類 CHandle

class CHandle : public CObject
{
public:
    HANDLE m_ThreadHandle;
};

關鍵實作細節

監控器建構:啟動監控線程

CThreadTask::CThreadTask()
{
    m_bKeepGoing = TRUE;
    DWORD tid = 0;
    m_Handle = (HANDLE)::CreateThread(NULL, 0, ThreadTaskProc, 0, 0, &tid);
}

登記句柄

void CThreadTask::AddHandle(CONST HANDLE h)
{
    CHandle* p = new CHandle;
    p->m_ThreadHandle = h;
    m_ObList.AddTail(p);
}

掃描清理核心邏輯

void CThreadTask::CloseThreadHandles()
{
    if (m_ObList.GetCount() == 0) return;

    POSITION pos1, pos2;
    CHandle* pHandle = NULL;
    DWORD dwExitCode = 0;

    for (pos1 = m_ObList.GetHeadPosition(); (pos2 = pos1) != NULL; )
    {
        pHandle = DYNAMIC_DOWNCAST(CHandle, m_ObList.GetNext(pos1));
        VERIFY(::GetExitCodeThread(pHandle->m_ThreadHandle, &dwExitCode));

        if (dwExitCode != STILL_ACTIVE) // 線程已結束
        {
            m_ObList.RemoveAt(pos2);
            VERIFY(::CloseHandle(pHandle->m_ThreadHandle));
            delete pHandle;
            pHandle = NULL;
        }
    }
}

監控線程函式

DWORD WINAPI ThreadTaskProc(LPVOID)
{
    HANDLE hCur = GetCurrentThread();
    SetThreadPriority(hCur, THREAD_PRIORITY_LOWEST); // 低優先級,不搶佔業務

    while (CThreadTask::GetThreadTask().m_bKeepGoing)
    {
        Sleep(500);
        CThreadTask::GetThreadTask().CloseThreadHandles();
    }

    SetThreadPriority(hCur, THREAD_PRIORITY_NORMAL);
    return 0;
}

管理器建構與啟動

CThreadManager::CThreadManager(ThreadProc proc, LPVOID param)
{
    _ASSERTE(CThreadTask::GetThreadTask().IsValid()); // 確保監控器就緒

    DWORD tid = 0;
    m_Handle = (HANDLE)::CreateThread(NULL, 0, proc, param, CREATE_SUSPENDED, &tid);
    // CREATE_SUSPENDED:建立後暫停,等 RunThread() 再恢復

    if (CThreadTask::GetThreadTask().m_bKeepGoing)
    {
        ASSERT(m_Handle);
        CThreadTask::GetThreadTask().AddHandle(m_Handle); // 登記給監控器
    }
}

BOOL CThreadManager::RunThread()
{
    return ::ResumeThread(m_Handle) != 0xFFFFFFFF;
}

使用範例

DWORD WINAPI MyWorker(LPVOID p)
{
    // 耗時任務...
    return 0;
}

void SomeFunction()
{
    CThreadManager mgr(&MyWorker, (LPVOID)this);
    mgr.RunThread(); // 線程啟動,結束後自動清理句柄,無需手動 CloseHandle
}

優缺點與改進方向

優點缺點/注意
使用極簡單:兩行代碼啟動自管線程監控線程輪詢 500ms,線程結束到句柄釋放有延遲
自動防洩漏:無需記住 CloseHandle無法取得線程退出碼 (若需可擴充 AddHandle 傳入回調)
封裝為 DLL 可跨專案重用MFC 依賴 (CObject, CObList),純 SDK 專案需調整
支援暫停啟動 (CREATE_SUSPENDED)單例監控器進程結束時需正確清理 (析構函數待完善)

改進建議

  • 改用 WaitForMultipleObjects + RegisterWaitForSingleObject (執行緒池) 替代輪詢,即時回收零延遲。
  • 加入 std::function/Lambda 支援 (C++11),擺脫靜態函式指標限制。
  • 提供 GetExitCode()、完成回調、例外傳播機制。
  • 移除 MFC 依賴,改用 STL + Windows API 實現跨平台編譯。

結語

「線程自管」思想將資源管理責任轉移給基礎設施層,業務代碼專注於邏輯。本文提供的 CThreadManager/CThreadTask 雖屬早期 MFC 風格,但核心模式 (生產者登記、專用回收器掃描) 仍具參考價值,可作為現代 C++ std::thread/std::jthread + 自定義 RAII 封裝的靈感來源。

本文配套原始碼可供下載。

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