C++封装线程类:实现暂停、恢复与安全销毁
在Windows C++开发中,封装具有完整生命周期的线程类可显著简化异步任务管理。本文将演示如何利用MFC的 CWinThread、事件对象(Event)和临界区(CriticalSection),实现一个支持状态检测、暂停、恢复及安全销毁的线程类。
核心设计思路
该封装方案旨在解决多线程编程中的常见痛点:线程状态的准确追踪、工作过程的挂起与唤醒、以及程序退出时资源的彻底清理。主要包含以下技术要点:
- 状态隔离:将“正在执行”(
m_bRunning)与“是否被请求继续”(m_bContinue)分离,避免竞态条件。 - 同步机制:使用
CRITICAL_SECTION保护标志位读写;使用CreateEvent实现主线程对工作线程的唤醒控制。 - 生命周期管理:析构函数中通过设置全局退出标志和触发事件,强制解除阻塞,并使用
WaitForSingleObject确保线程完全退出发后释放句柄。
第1步:定义类接口与成员变量
首先定义类的公共接口和内部私有/受保护成员。外部调用者主要通过 IsRunning()、Startxxx()、Suspendxxx() 等方法与线程交互。
#include <afxwin.h>
// 自定义消息,用于向主窗口汇报进度或错误
#define WM_CUTTERSTART (WM_USER + 100)
class CMyClass
{
public:
enum ExitCode {
exitSuccess, // 成功完成任务
exitUserForce, // 用户强制终止
exitError // 运行出错
};
explicit CMyClass(HWND hWndNotify);
~CMyClass();
// 状态查询
BOOL IsRunning() const { return m_bRunning; }
// 控制操作
BOOL StartTask();
BOOL SuspendTask();
BOOL ResumeTask();
void StopTask();
protected:
// 核心工作函数(子类重写此方法填入实际逻辑)
virtual void DoSomething();
// 辅助重置函数
void Reset();
private:
// 线程入口点
static UINT _ThreadEntry(LPVOID lpParam);
HWND m_hWndNotify; // 接收消息通知的窗口句柄
HANDLE m_hWorkEvent; // 唤醒工作线程的事件
CWinThread* m_pThread; // MFC线程对象
CRITICAL_SECTION m_cs; // 互斥锁,保护共享状态
BOOL m_bRunning; // 当前是否正在执行DoSomething
BOOL m_bContinue; // 允许DoSomething继续执行的标志
BOOL m_bSuspend; // 是否处于暂停状态
BOOL m_bExitThread; // 全局退出信号
};
第2步:实现构造函数与初始化
在构造函数中,创建自动重置的事件对象和临界区。MFC的 AfxBeginThread 默认线程会自动删除自身,这里需禁用自动删除以便手动管理生命周期,并立即启动线程进入等待状态。
CMyClass::CMyClass(HWND hWndNotify)
{
m_hWndNotify = hWndNotify;
m_bExitThread = FALSE;
m_bSuspend = FALSE;
// 创建事件对象:自动重置,初始未置位
m_hWorkEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
// 创建关键代码段
::InitializeCriticalSection(&m_cs);
// 初始化状态标志
m_bContinue = TRUE;
m_bRunning = FALSE;
// 启动线程(CREATE_SUSPENDED确保线程先挂起,但需要Resume才能真正运行入口点逻辑)
// 注意:此处传入 this 指针供静态函数获取对象实例
m_pThread = AfxBeginThread(_ThreadEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
if(m_pThread) {
m_pThread->m_bAutoDelete = FALSE; // 禁止自动删除
m_pThread->ResumeThread(); // 恢复线程以进入入口循环
}
}
第3步:实现线程入口与核心循环
线程入口函数 _ThreadEntry 是一个死循环。它主要做两件事:等待外部触发(SetEvent)以及执行具体业务逻辑(DoSomething)。在每次工作结束后,通过 Reset() 清理状态。
UINT CMyClass::_ThreadEntry(LPVOID lpParam)
{
CMyClass* pThis = static_cast<CMyClass*>(lpParam);
while(TRUE)
{
// 等待唤醒信号或退出信号
DWORD waitResult = ::WaitForSingleObject(pThis->m_hWorkEvent, INFINITE);
// 如果收到退出信号,直接返回结束线程
if(pThis->m_bExitThread || waitResult != WAIT_OBJECT_0)
break;
// 标记线程进入工作状态
::EnterCriticalSection(&pThis->m_cs);
pThis->m_bRunning = TRUE;
::LeaveCriticalSection(&pThis->m_cs);
// 执行业务逻辑
pThis->DoSomething();
// 准备下一次循环
pThis->Reset();
}
return 0;
}
第4步:封装暂停与恢复功能
利用MFC线程对象的 SuspendThread 和 ResumeThread API。需注意判断当前是否处于 m_bRunning 状态,非运行状态下暂停/恢复无意义且可能报错。
// 暂停线程
BOOL CMyClass::SuspendTask()
{
if(!m_bRunning) return FALSE;
if(!m_bSuspend)
{
m_pThread->SuspendThread();
m_bSuspend = TRUE;
}
return TRUE;
}
// 恢复线程
BOOL CMyClass::ResumeTask()
{
if(!m_bRunning) return FALSE;
if(m_bSuspend)
{
m_pThread->ResumeThread();
m_bSuspend = FALSE;
}
return TRUE;
}
第5步:安全的析构与资源释放
这是最关键的部分。析构函数必须确保:1. 告诉工作线程停止执行(m_bContinue = FALSE);2. 如果线程正卡在 WaitForSingleObject,必须立刻将其唤醒;3. 阻塞直到线程句柄关闭;4. 销毁临界区和句柄。
CMyClass::~CMyClass()
{
// 1. 设置全局退出标志
m_bExitThread = TRUE;
// 2. 设置取消标志,中断正在进行的 DoSomething()
::EnterCriticalSection(&m_cs);
m_bContinue = FALSE;
::LeaveCriticalSection(&m_cs);
// 3. 【重要】唤醒正在 Wait 的线程,使其跳出 while 循环
::SetEvent(m_hWorkEvent);
// 4. 等待线程完全结束
::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
// 5. 释放所有系统资源
::CloseHandle(m_hWorkEvent);
::DeleteCriticalSection(&m_cs);
delete m_pThread;
}
总结
通过上述封装,开发者获得了一个类似“即插即用”的后台处理单元。外部只需调用 StartTask、SuspendTask、ResumeTask 即可灵活控制进程。在实际使用中,请继承此类并重写 DoSomething 方法以实现具体的业务逻辑,并注意在处理长耗时操作时需频繁检查 m_bContinue 以确保即时响应停止指令。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













