多线程管理类

实现思路

由于最近经常搞些跟线程有关的东西,感觉多线程确实麻烦,线程间要处理好同步与通讯,如果用CWinThread好一点,直接是一个线程对象,如果 用AfxBeginThread,那必须定个全局函数,或者写个静态函数,一般是传个this指针进去,然后再用这个指针调用本类函数的成员函数,用起来 比较麻烦,现在问题是能不能不用全局或者静态函数来实现呢,于是我实现了这个类,来所简化多线程的创建和关闭的操作。

现在简要看一下类的数据和成员函数

template<class T>
class CYGMulThread
{
protected:
	typedef void (T::* _pThreadFunc)(void );
	static _pThreadFunc ThreadFunc;
	static BOOL s_IsBreaking; //用以判断线程是否要中断
	static CWaitDlg s_Waitdlg;//等待对话框 
	//内部线程
	static UINT ThreadProc(LPVOID lpParam);
public:
	//在线程内部使用,用以判断有没有要结束的信号
	static BOOL IsBreakThread() ; 
	//----------------------------非静态
protected:
	CYGMulThread();
	virtual ~CYGMulThread();
	CWinThread **m_pThread;//线程数组指针
	int m_nThreadCount;
	//处理windows的消息
	void PeekMessageLoop();
public:
	int GetThreadCount() const ; 
	//pFunc=输入一个void f(void)的成员函数,nThreadCount=线程数目
	void YGBeginFuncThread(_pThreadFunc pFunc,int nThreadCount);
	//结束线程时调用
	bool EndAllThread(const char *szMsg);
};

使用方法

这里先说一下这个类的使用方法

class CMulThreadDlg : public CDialog,private CYGMulThread
{
// Construction
public:
	CMulThreadDlg(CWnd* pParent = NULL);	// standard constructor
	void ReleaseShow2();
	void ReleaseShow();
	void ThreadFunc();
// Dialog Data
	//{{AFX_DATA(CMulThreadDlg)
	enum { IDD = IDD_MULTHREAD_DIALOG };
	CYGEdit	m_edShow;
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMulThreadDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	HICON m_hIcon;
	// Generated message map functions
	//{{AFX_MSG(CMulThreadDlg)
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnBtnDebug();
	afx_msg void OnBtnStop();
	afx_msg void OnBtnTest1();
	afx_msg void OnBtnTest2();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

这里要注意两个地方:

  1. CYGMulThread的模板是用被派生出来的类,这里的好处,就是可以使CYGMulThread可以使用被派生出来的东西,至于还有什么好处,有兴趣的朋友可以看一下ATL和WTL里的代码,里面大量使用这种结构。
  2. 这里用的私有继承,当然也可以用公用继承:),个人觉得这样私有继承,数据封装性好一点。

CMulThreadDlg有了这样的头定义后,就可以使用下面的行为了

void CMulThreadDlg::ThreadFunc()
{
	DWORD dwID=GetCurrentThreadId();
	while (1)
	{
//		g_cs1.Lock();
		TRACE("线程:%x/t路过/n",dwID);
//		g_cs1.Unlock();
		Sleep(200);
		if (IsBreakThread()) 
		{	
//			g_cs1.Lock();
			TRACE("线程:%x要结束了!/n",dwID);
//			g_cs1.Unlock();
			return ;
		}
		Sleep(10000);	
	}
}
//创建线程
void CMulThreadDlg::OnBtnDebug() 
{
    YGBeginFuncThread(ThreadFunc,10);//创建10个线程,其函数为ThreadFunc,这里ThreadFunc是CMulThreadDlg的成员函数
} 
//结束线程
void CMulThreadDlg::OnBtnStop() 
{
	EndAllThread("正要结束线程....");
} 

代码内容

下面说明一下代码的内容。

其实创建线程的操作很简单,最令人烦不胜烦的地方是结束线程的代码。一般其流程是:

  1. 通知所以线程要结束
  2. 等待所以线程结束

通知其实倒挺好通知,问题是等待!

第一个问题,主线程界面,考虑的第一个函数是WaitForMultipleObjects,问题是主线用这个函数等待子线程结束时,令主线程会处于休眠 状态,如果等待时间长的话,程序就会像死了似的。于是把函数换为MsgWaitForMultipleObjects,令其每次只等待一个线程,而且将其 参数bWaitAll设为FALSE,并在调用这个函数前,用PeekMessageLoop处理完Windows留下的消息,这样主界面看起来就不会出问题 。

第二个问题,句柄的复制,因为在MsgWaitForMultipleObjects里,需要输入要等待线程的句柄数组,但AfxBeginThread开启的线程,当线程结束后,其线程句柄就无效,句柄不能普通地赋值,只能用DuplicateHandle将其复制 。

第三个问题,提示,当主线程长时间等待子线程时,是不是应有个提示框之类的东西提示呢,然后主主界面的任何其它操作都不能用,就像我们平时用 MessageBox时,我们只能先把这个Box去掉之后,才能操作界面的其它东西,但这里不用这个方法,这里只能Create一个非模态对话框,但这个 框要达到像模态对话框的效果,我用spy++看了一下才看想到原来可以这样

//pWnd是主界面的指针
pWnd->EnableWindow(false);
//Create一个非模态对话框
//等待所以线程结束 
pWnd->EnableWindow(true);
pWnd->ShowWindow(SW_RESTORE); 
 下面是主要代码:
bool EndAllThread(const char *szMsg)
{
	if (!m_pThread) return false;//线程句柄为空
	HANDLE pProcess=GetCurrentProcess();
	BOOL bRet;
	int i(0),nRet;
	HANDLE *pHandle=new HANDLE[m_nThreadCount];
	T *pWnd=static_cast<T *>(this); 
	pWnd->EnableWindow(false);
	
	if (szMsg)
	{
		s_Waitdlg.m_strshow=szMsg;
		s_Waitdlg.SetCancelDisable();//使取消按钮无效
		s_Waitdlg.Create((char *)IDD_YGDIALOG_WAITING,pWnd);
		s_Waitdlg.CenterWindow(NULL);
		s_Waitdlg.ShowWindow(SW_SHOW);
	}
	for(i=0;i<m_nThreadCount;i++) 
	{
		//将句柄复制
		bRet=DuplicateHandle(pProcess,m_pThread[i]->m_hThread,pProcess,&pHandle[i],DUPLICATE_SAME_ACCESS,
			true,DUPLICATE_SAME_ACCESS);
		//保证所复制的句柄都要有效
		if (!bRet)
		{
			Sleep(100);
			TRACE("----------------DuplicateHandle 失败!/n");
			i--;continue;
		}
	}
	s_IsBreaking=true;//将线程结束
	i=0;
	//等待线程结束
	while (i!=m_nThreadCount)//直到所以线程的m_hThread都已经结束时则i==m_nThreadCount
	{
		for(i=0;i<m_nThreadCount;i++) 
		{
			PeekMessageLoop();//先处理多余的Windows消息
			//这里一定要用MsgWaitForMultipleObjects,不能用WaitForMultipleObjects,因为这个不能处理消息
			nRet=MsgWaitForMultipleObjects(1,pHandle+i,false,INFINITE,QS_ALLEVENTS);
			TRACE("nRet=%d,i=%d/n",nRet,i);
			if (nRet==WAIT_OBJECT_0+1) //只有消息Event,没有线程的Event
			{
				Sleep(20); //等会儿
				break;
			}
			if (nRet==WAIT_FAILED ) break;//发生错误,无须等待了
		}
		if (nRet==WAIT_FAILED ) break;
	}
	delete [] m_pThread;
	delete [] pHandle;
	m_pThread=NULL;
	m_nThreadCount=0;
	pWnd->EnableWindow(true);
	pWnd->ShowWindow(SW_RESTORE);
	if (IsWindow(s_Waitdlg.m_hWnd)) s_Waitdlg.DestroyWindow();
	if (nRet==WAIT_FAILED ) false;
	return true;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值