当前位置:C++技术网 > 资讯 > worker线程与UI线程的学习

worker线程与UI线程的学习

更新时间:2015-09-30 18:48:36浏览次数:1+次

    首先我们来写个UI界面线程程序:

    新建一个VC基于单文档的项目(Thread),我们在Menu(菜单)中,添加一个菜单项-线程,然后在其下拉菜单中添加子菜单项-UI界面线程,ID号为ID_UI_THREAD。然后我们添加个新类MyThread,我们在其InitInstance()函数中写上代码:

BOOL MyThread::InitInstance()
{
	// TODO:  perform and per-thread initialization here
	CFrameWnd* pFrameWnd= new CFrameWnd();
	pFrameWnd->CreateEx(0,AfxRegisterWndClass( CS_HREDRAW|CS_VREDRAW) ,
				"用户界面线程示例",
				WS_OVERLAPPEDWINDOW|WS_VISIBLE,
				CRect(100,100,400,300),
				NULL,
				0);
		pFrameWnd->ShowWindow(SW_SHOW);
		pFrameWnd->UpdateWindow();
	return TRUE;
}

最后我们为我们的UI界面线程子菜单项添加类向导,创建消息响应函数,写上如下代码:

void CThreadView::OnUiThread() 
{
	// TODO: Add your command handler code here
	AfxBeginThread(RUNTIME_CLASS(MyThread));
}

    下面我讲解下代码原理,同时引出worker线程与UI界面线程:

 CFrameWnd* pFrameWnd= new CFrameWnd();

这句代码为我们从堆上分配一个指向CFrameWnd类(通俗点就是主窗口类)的指针,用来调用CreateEx来创建窗口,也就是我们的用户界面线程的界面。
CFrameWnd类往往用于创建应用程序的主窗口。该类定义了两个成员函数用于创建主窗口,即Create()和LoadFrame()。前者主要通过CWnd::CreateEx()创建窗口;而后者首先组织参数,再调用前者。我们看看CreateEx函数:

BOOL CreateEx(
DWORD dwExStyle,
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
LPVOID lpParam = NULL
);

参数解析:
dwExStyle
指定要建立的窗口的扩展样式,引扩展样式包括所有的MFC样式。
lpszClassName
创建窗口的类名称,类的名称可以用全局AfxRegisterWndClass函数或预定义的注册任何类名称。
lpszWindowName
创建的窗口名称,为自定义字符串,如:"我的窗口"(含引号)
dwStyle
指定的窗口样式属性。见窗口样式和所调用CWnd::描述值创建样式的所有值,用"|"将不同值连起来,如:OPUP | WS_SYSMENU|WS_EX_TOOLWINDOW
RECT& rect
所要创建窗口的坐标、高度及宽度或都用矩形表示
hWndParent
标识的父窗口或所属窗口。无父窗口时使用空值:"NULL"
nIDorHMenu
标识所创建窗口的菜单,可使用菜单ID:"IDR_MENU"或使用空值:"NULL"
lpParam
以由CREATESTRUCT结构lpCreateParams领域中引用的数据
nID
本窗口作为子窗口使用时的ID号。
对于AfxRegisterWndClass函数不懂的话,请看本站的《零基础学windows》。
MFC支持两种类型的线程:工作者线程和用户界面线程。
工作者线程:常用于完成不需要用户输入的任务。如耗时计算、后台打印之类的任务。因此,其不需要界面。
用户界面则相反,通常用于处理用户的输入,相应胡勇产生的消息,需要用户界面。
MFC对这两种线程有重大的区别,虽然两种线程都是以AfxBeginThread()启动,但是MFC利用C++函数的overloading性质,对该函数提供了两种不同的声明。(注:C++函数overloading就是函数重载的特性)
AfxBeginThread()的第一种形态是用来启动一个worker线程。

CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

参数:
pfnThreadProc 函数名称,用来启动线程。
pParam 任意4字节数值,用来传给新线程。它可以是个整
数,或指针,或单纯只是个 0。
nPriority 新线程的优先权。如果是 0(缺省值),表示新线
程的优先权将与目前优先权相同。
nStackSize 新线程的堆栈大小(字节) 。和 CreateThread() 一
样, 0 表示使用默认的堆栈大小。
dwCreateFlags 此值必须为 0 或 CREATE_SUSPENDED 。如果
你省略它,表示标记为 0,也就是立刻开始新线程
的生命。
lpSecurityAttrs CreateThread() 所需的安全属性。在 Wind ows 95
中无效。

AfxBeginThread() 第二版本

CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);

 

参数
pThreadClass 指向你所产生的一个类的 runtim e class 。该类
派生自 CWi nThread。
nPriority 新线程的优先权。如果是 0(缺省值),表示新线程将
拥有和目前线程相同的优先权。
nStackSize 新线程的堆栈大小(字节)。和 CreateThread() 相同,
此值若为 0,表示使用默认的堆栈大小。
dwCreateFlags 此标记值若不为 0 就是 CREATE_S USPENDED。如
果遗漏未写,默认为 0,于是线程在产生之后立刻开始
执行。
lpSecurityAttrs 安 全 属 性 , 就 像 Create Thread() 所 需 的 一 样 。 在
Windows 95 中无效。

一般来说,用户界面线程的创建过程需要先从基类CWinThread中派生出用户自定义的一个新类,在使用AfxBeginThread()函数创建线程。这就是该函数的第一个参数中“只想你所产生的一个类的runtime class”的意思(也就是我们这个程序中的MyThread类。

一旦线程开始执行 , MFC 将调用你的 InitInstance() 成员函数,并进入消
息循环中。你的 CWinT hread 派生类的消息映射表( message map)将被作为
消息派送的指引。

最后看下实现结果吧:

看我们创捷成功用户界面线程了。