Win32多线程 -- MFC中的线程

警告
如果要在 MFC 程序中产生一个线程, 而该线程将调用 MFC 函数或使用 MFC 的任何数据,那么你必须以 AfxBeginThread或CWinThread::CreateThread来产生这些线程。

worker 线程和 GUI 线程
Win32 两者一般而言都是以 CreateThread 或 _beginthreadex 开始其生命. 如果线程调用 GetMessage 或 CreateWindow 之类函数, 消息队列便会产生, 而 worker 线程也就摇身一变成了 GUI 线程(或称为 UI 线程)

一、在MFC中启动一个Worker线程

AfxBeginThread的第一种形式用来启动一个worker线程
CWinThread* AfxBeginThread(
    AFX_THREADPROC pfnThreadProc, // 线程函数
    LPVOID pParam, // 线程参数
    int nPriority = THREAD_PRIORITY_NORMAL, // 优先权
    UINT nStackSize = 0, // 堆栈大小, 0表示默认值
    DWORD dwCreateFlags = 0, // 此值必须为 0 或 CREATE_SUSPENDED 
    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL // CreateThread 所需的安全属性
);
如果失败, 传回NULL; 否则传回一个指针, 指向新产生出来的CWinThread对象。

1. 以 AfxBeginThread() 产生 worker 线程

#include <afxwin.h>

CWinApp TheApp;
UINT ThreadFunc(LPVOID);

int main()
{
	for (int i=0; i<5; i++)
 	{
	if (AfxBeginThread( ThreadFunc, (LPVOID)i ))
 		printf("Thread launched %d\n", i);
	}

 	// Wait for the threads to complete.
 	Sleep(2000);

 	return 0;
}

UINT ThreadFunc(LPVOID n)
{
	for (int i=0;i<10;i++)
	printf("%d%d%d%d%d%d%d%d\n",n,n,n,n,n,n,n,n);
 	return 0;
}

2. 安全地使用AfxBeginThread的传回值

默认情况下, 当线程结束时, CWinThread 对象会自动被删除, 这是因为 MFC 安插了它自己的线程启动函数, 并且更在你自己的线程函数之前, 于是可以承担清理工作。用以删除 CWinThread 对象的那个清理函数,同时也关闭了线程的 handle. 当线程结束其生命时, 它的 handle也就被关闭了。如果从线程启动到结束所花费的时间很短, CWinThread 对象可能在 AfxBeginThread() 返回时就已经被砍了。在这种情况下, 任何操作只要触及线程的 handle, 就会让你的程序当掉。
CWinThread 中有一个成员变量 m_bAutoDelete, 这个参数可以阻止CWinThread 对象被自动删除, 那么你就得自己删除之. 为了能够设定此变量而不产生一个 race condition, 你必须先以挂起状态产生线程。不用调用 CloseHandle 关闭线程的handle, CWinThread的析构函数会自动完成此事, 不管 m_bAutoDelete 是否被设立。

#include <afxwin.h>
CWinApp TheApp;
UINT ThreadFunc(LPVOID);
int main()
{
	CWinThread* pThreads[5];
	for (int i=0; i<5; i++)
	{
		pThreads[i] = AfxBeginThread(
						ThreadFunc,
						(LPVOID)i,
						THREAD_PRIORITY_NORMAL,
						0,
						CREATE_SUSPENDED // 挂起线程
					);
		ASSERT(pThreads[i]);
		pThreads[i]->m_bAutoDelete = FALSE; // 设置为不自动删除
		pThreads[i]->ResumeThread(); // 运行线程
		printf("Thread launched %d\n", i);
	}

	for (i=0; i<5; i++)
	{
		WaitForSingleObject(pThreads[i]->m_hThread, INFINITE); // 等待线程结束
		delete pThreads[i]; // 删除线程对象
	}
	return 0;
}

UINT ThreadFunc(LPVOID n)
{
	for (int i=0;i<10;i++)
		printf("%d%d%d%d%d%d%d%d\n",n,n,n,n,n,n,n,n);
	return 0;
}

3. 对CWinThread进行subclassing

#include <afxwin.h>
CWinApp TheApp;

class CUserThread : public CWinThread
{
public: // Member functions
	CUserThread(AFX_THREADPROC pfnThreadProc);
	
	static UINT ThreadFunc(LPVOID param);
	
public: // Member data
	int m_nStartCounter;
	
private: // The "real" startup function
	virtual void Go();
};

CUserThread::CUserThread(AFX_THREADPROC pfnThreadProc):CWinThread(pfnThreadProc, NULL) // Undocumented constructor
{
	m_bAutoDelete = FALSE;
	// Set the pointer to the class to be the startup value.
	// m_pThreadParams is undocumented,
	// but there is no work-around.
	m_pThreadParams = this;
}

int main()
{
	CUserThread* pThreads[5];
	for (int i=0; i<5; i++)
	{
		// Pass our static member as the startup function第 10 章 ■ MFC 中的线程 287
		pThreads[i] = new CUserThread( CUserThread::ThreadFunc );
		// Set the appropriate member variable
		pThreads[i]->m_nStartCounter = i;
		// Start the thread in motion
		VERIFY( pThreads[i]->CreateThread() );
		printf("Thread launched %d\n", i);
	}
	for (i=0; i<5; i++)
	{
		WaitForSingleObject(pThreads[i]->m_hThread, INFINITE);
		delete pThreads[i];
	}
	return 0;
}

// static
UINT CUserThread::ThreadFunc(LPVOID n)
{
	CUserThread* pThread = (CUserThread*)n;
	pThread->Go();
	return 0;
}

void CUserThread::Go()
{
	int n = m_nStartCounter;
	for (int i=0;i<10;i++)
		printf("%d%d%d%d%d%d%d%d\n",n,n,n,n,n,n,n,n);
}

二、在MFC中启动一个UI线程

AfxBeginThread第二版本
CWinThread* AfxBeginThread(
    CRuntimeClass* pThreadClass, // 指向一个派生自CWinThread的runtime class 类
    int nPriority = THREAD_PRIORITY_NORMAL,
    UINT nStackSize = 0,
    DWORD dwCreateFlags = 0,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
AfxBeginThread传回 NULL 表示失败。否则, 它会传回一个指针, 指向新的 CWinThread 对象。
CWinThread 提供了一堆多样化的虚函数, 你可以改写之以帮助消息的处理、线程的启动(startup)和清理(cleanup), 以及异常情况的处理(exception handling)。这些虚函数列举如下:
    InitInstance
    ExitInstance
    OnIdle
    PreTranslateMessage
    IsIdleMessage
    ProcessWndProcException
    ProcessMessageFilter
    Run
默认情况下只要改写 InitI nstance(),MFC就会开启一个消息循环。

1. 以 ClassWizard 产生一个 UI 线程

本例之中, ClassWizard 会为我们产生两个文件, 分别是 DemoThread.cpp和DemoThread.h,并把它们加到项目之中。在 DemoThread.cpp 中, 你会发现 ClassWizard 已经帮我们产生了 InitInstance 和 ExitInstance, 并为该线程产生出最上层的消息映射表(message map)。

class CDemoThread : public CWinThread
{
	DECLARE_DYNCREATE(CDemoThread)
protected:
	CDemoThread(); // protected constructor used by dynamic creation
	// Attributes
public:
	// Operations
public:
	// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CDemoThread)
public:
	virtual BOOL InitInstance();
	virtual int ExitInstance();
	//}}AFX_VIRTUAL

	// Implementation
protected:
	virtual ~CDemoThread();
	// Generated message map functions
	//{{AFX_MSG(CDemoThread)
	// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

在类声明一开始处, 你会看到 DECLARE_DYNCREATE() 宏的使用。这个宏实现出运行时类型识别(RTTI)和动态生成(Dynamic Creation), 同时也是让 CD emoThread 与 AfxBeginT hread() 合作的关键。在 DemoThread.cpp中你还会看到IMP LEMENT_DYNCREATE()宏. DECLARE_xxx 和IMPLEMENT_xxx 两宏总是成双成对地被使用。

2. 启动 UI 线程

一旦 wizard 产生出线程类以及 DYN CREATE 宏,你就应该准备使用AfxBeginThread()了。下面是个范例:
CDemoThread* pThread = (CDemoThread*)AfxBeginThread( RUNTIME_CLASS(CDemoThread) );

其中作为参数的,是一个数据结构(即 CRuntimeClass 对象), 允许 MFC 在运行时产生该类的一个实体。就像 wo rker 线程的情况一样,当你以 AfxBeginThread 产生一个 UI 线程时, 你就让这个函数去配置数据结构并传回。你也可以在你的构造函数中设定 m_AutoDelete为 FALSE 。这一次会比较容易些, 因为你已经派生了这一类, 并且构造函数将被AfxBeginThread调用。一旦线程开始执行, MFC 将调用你的 InitInstance 成员函数, 并进入消息循环中。你的 CWinThread 派生类的消息映射表(message map)将被作为消息派送的指引。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值