Windows下C++多线程管理类
windows下通过手动调用WIN32 API里的CreateThread这些函数来管理线程,还需要管理线程同步,着实不方便。借鉴Qt中的QThread的思路。自定义一个CThread,作为线程对象的基类。
以下是Thread.h的代码
#pragma once
#include <exception>
#include <WinBase.h>
class CThread;
//定义了CThread类的Priority属性的可能值,如下所述。根据优先级确定每个线程CPU周期
enum CThreadPriority
{
tpIdle = THREAD_PRIORITY_IDLE, //只有当系统空闲时该线程执行
tpLowest = THREAD_PRIORITY_LOWEST, //线程优先线比正常低2点
tpLower = THREAD_PRIORITY_BELOW_NORMAL, //线程优先线比正常低1点
tpNormal = THREAD_PRIORITY_NORMAL, //线程优先线比正常低
tpHigher = THREAD_PRIORITY_ABOVE_NORMAL, //线程优先线比正常高1点
tpHighest = THREAD_PRIORITY_HIGHEST, //线程优先线比正常高2点
tpTimeCritical = THREAD_PRIORITY_TIME_CRITICAL //线程优先线最高
};
//线程通知消息类
class CThreadNotifyEvent
{
public:
//当线程的Execute方法已经返回且在该线程被删除之前发生
virtual void OnTerminate(CThread *thread) = 0;
//当线程发生异常时发生
virtual void OnException(std::exception &e) = 0;
};
// CThread是一个抽象类,可以创建几个独立的线程
// 每一新子类的CThread对象的实例是一个新的线程。从CThread派生的多线程实例可以构成多线程应用程序
// 当一个应用程序运行时,应用程序就被载入内准备执行。此时,它成为包含一个或多个线程的进程,每个
// 线程含有数据、代码和系统资源。线程执行应用程序的部分内容,并由系统分配CPU时间。同一进程的所
// 有线程共享同一地址空间,可以访问进程的全局变量。线程通过以下工作改善应用的性能:
// 管理多通信设备的输入
// 区分任务的优先级。优先级高的处理紧争的任务,优先级低的处理其他任务
// 以下是使用线程的一些建议:
// 同时跟踪太多的线程消耗CPU时间。对单处理器系统,一个进程最多有16个线程
// 当多个线程更新相同的资源时,应使线程同步以避免冲突
// 大多数访问系统对象和更新窗体的方法必须从主系统线程内部调用
class CThread
{
public:
//创建一个线程对象的实例,并挂起。Execute直到Resume调用后才被调用
CThread();
virtual ~CThread();
bool IsActive(); //线程是否已经激活
bool IsTerminated(); //如果IsTerminated == true说明进程要求该线程中止。
bool Resume(); //重新执行一个挂起的线程。成功返回true;
bool Suspend(); //挂起一个运行中的线程。成功返回true;
void Terminate(); //终止线程
void Wait(); //等待线程结果
HANDLE GetHandle(); //返回线程句柄
CThreadPriority GetPriority();//返回线程优先级
void SetPriority(CThreadPriority priority); //设置该线程相对于进程中其他线程的优先级
void Attach(CThreadNotifyEvent *event); //注册线程通知消息event
//设置信号量,相当于UnLock
void SetEvent();
//释放信号量,相当于Lock
void ResetEvent();
protected:
//Execute模板
//while (!IsTerminated())
//{
// Wait(); //等待事件有信号状态(unlocked)
// if (IsTerminated()) //需要再次判断,因为Wait之后,IsTerminated可能返回了true
// return;
// ResetEvent(); //将信号设置成无信号状态(Lock),根据实际情况,也可以不加锁
// // TODO: Add your code here
// SetEvent(); //将信号设置成有信号状态(Unlock),根据实际情况,和上面的加锁匹配
//}
virtual void Execute() = 0;
private:
CThreadNotifyEvent *m_threadNotifyEvent; //观察者
HANDLE m_hHandle; //线程句柄
HANDLE m_hWaitEvent; //事件对象句柄
DWORD m_dwThreadID; //线程ID
bool m_bActive; //标识线程是否处于激活状态
bool m_bTerminated; //标识是否需要结束线程
//!所有线程必须从一个指定的函数开始执行,该函数称为线程函数,它必须具有下列原型:
static DWORD WINAPI ThreadProc(LPVOID lpvThreadParm);
};
接着是Thread.cpp
#include "stdafx.h"
#include "Thread.h"
typedef unsigned(__stdcall *PTHREAD_START) (void *);
CThread::CThread()
{
m_threadNotifyEvent = nullptr;
m_bTerminated = false;
m_bActive = false;
m_dwThreadID = 0;
m_hHandle = (HANDLE)_beginthreadex(NULL, 0, (PTHREAD_START)ThreadProc,
this, CREATE_SUSPENDED, (unsigned *)&m_dwThreadID);
if (m_hHandle == nullptr)
{
char msg[64];
sprintf_s(msg, "Create thread failed with error code %d.\n", GetLastError());
throw std::exception(msg);
}
//安全属性:NULL, 复位方式:手动, 初始状态:有信号状态
m_hWaitEvent = ::CreateEvent(nullptr, TRUE, TRUE, NULL);
if (m_hWaitEvent == nullptr)
{
char msg[64];
sprintf_s(msg, "Create event failed with error code %d.\n", GetLastError());
throw std::exception(msg);
}
}
CThread::~CThread(void)
{
this->Terminate();
::CloseHandle(m_hWaitEvent);
::CloseHandle(m_hHandle);
}
bool CThread::IsActive()
{
return m_bActive;
}
bool CThread::IsTerminated()
{
return m_bTerminated;
}
bool CThread::Resume(void)
{
if (m_bActive)
return true;
if (::ResumeThread(m_hHandle) == -1)
{
char msg[128];
sprintf_s(msg, "Resume thread failed with error code %d.\n", GetLastError());
throw std::exception(msg);
return false;
}
m_bActive = true;
return true;
}
bool CThread::Suspend(void)
{
if (!m_bActive)
return true;
if (SuspendThread(m_hHandle) == -1)
{
char msg[128];
sprintf_s(msg, "Suspend thread failed with error code %d.\n", GetLastError());
throw std::exception(msg);
return false;
}
m_bActive = false;
return true;
}
void CThread::Terminate()
{
//这里加锁,确保调用Terminate函数之后,Execute函数能够立即停止运行
Wait();
ResetEvent(); //Lock
m_bTerminated = true;
SetEvent(); //Unlock,设置成有信号状态
}
void CThread::Wait()
{
//函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去
::WaitForSingleObject(m_hWaitEvent, INFINITE);
}
HANDLE CThread::GetHandle()
{
return m_hHandle;
}
CThreadPriority CThread::GetPriority()
{
return (CThreadPriority)GetThreadPriority(m_hHandle);
}
void CThread::SetPriority(CThreadPriority priority)
{
SetThreadPriority(m_hHandle, priority);
}
void CThread::Attach(CThreadNotifyEvent *event)
{
m_threadNotifyEvent = event;
}
void CThread::SetEvent() //Unlock
{
::SetEvent(m_hWaitEvent);
}
void CThread::ResetEvent() //Lock
{
::ResetEvent(m_hWaitEvent);
}
DWORD WINAPI CThread::ThreadProc(LPVOID lpvThreadParm)
{
CThread *thread = (CThread*)lpvThreadParm;
CThreadNotifyEvent *event = thread->m_threadNotifyEvent;
try
{
thread->Execute();
}
catch (std::exception &e)
{
if (event)
event->OnException(e);
}
try
{
if (event)
event->OnTerminate(thread);
}
catch (std::exception &e)
{
if (event)
event->OnException(e);
}
return 0;
}
接下来是用法
比如,我们创建了一个MFC对话框应用程序,需要接收串口消息。我截取关键代码。
- 对话框类继承上述CThread类,并重写Execute函数,头文件主要代码如下:
class CDemoDlg : public CDialogEx, CThread
{
//省略其他代码
//从CThread中继承的虚函数
protected:
virtual void Execute() override;
}
- 重写Execute函数,cpp文件主要代码如下
void CDemoDlg::Execute()
{
//模板
while (!IsTerminated())
{
Wait(); //等待事件有信号状态(unlocked)
if (IsTerminated()) //需要再次判断,因为Wait之后,IsTerminated可能返回了true
return;
ResetEvent(); //将信号设置成无信号状态(Lock),根据实际情况,也可以不加锁
// TODO: Add your code here
//省略需要反复执行的代码
Sleep(1);
SetEvent(); //将信号设置成有信号状态(Unlock),根据实际情况,和上面的加锁匹配
}
}
注意:上述Execute函数中,最后有一个Sleep(1);这句代码可有效降低CPU占用,可以根据实际情况增大时间间隔,当然在我的实际情况下,时间间隔的大小对CPU占用率影响不大。