[普通]MFC多线程续篇

作者(passion) 阅读(1053次) 评论(0) 分类( 软件)

一般MFC应用程序的主线程是CWinApp派生类的对象,是由MFC APP Wizard自动创建的,这一节我们就说一下创建其他用户界面线程所必须的步骤。

用户界面线程与工作线程一样,都使用由操作系统提供的管理新线程的机制。但用户界面线程允许使用MFC提供的其他用户界面对象,如对话框或者窗口。相应的,为了使用这些功能,我们必须做更多的工作。创建并启动用户界面线程一般要经过3个步骤:

1.CWinThread类派生出自己的线程类

2.改造这个线程类,是它能够完成用户所希望的工作

3.创建并启动用户界面线程

1.CWinThread类派生出自己的线程类

要创建一个MFC的用户界面线程,所要做的第一件事就是从CWinThread来派生出自己的线程类,一般借助ClassWizard来做这项工作。

2.改造自己的线程类

对这个派生的线程类做以下改造工作

1.在这个线程的头文件中,使用DECLARE_DYNCREATE宏来声明这个类,在cpp中使用IMPLEMENT_DYNCREATE宏来实现这个类

2.这个线程类必须重载它的基类(CWinThread)的某些成员函数,如该类的InitInstance成员函数;对于基类的其他的成员函数,可以有选择的重载,也可以使用由CWinThread类提供的缺省函数

3.创建新的用户界面窗口类,如窗口、对话框,并添加所需要的用户界面控件,然后建立新建的线程类与这些用户界面窗口类联系

4.利用类向导,为新建的线程类添加控件成员变量,添加响应消息的成员函数,为它们编写实现的代码

3.创建并启动用户界面线程

要创建并启动用户界面线程,可以使用MFC提供的AfxBeginThread函数的另一个版本,

函数原型:

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

其中参数pThreadClass是一个指向CRuntimeClass类的对象指针,该类是从CWinThread类继承而来。用户界面线程的运行时类就是在第一步骤从CWinThread派生的线程类,本参数就指向它,在实际调用时,一般使用RUNTIME_CLASS宏将线程类指针转换为指向CRuntimeClass对象的指针,宏的调用格式如下

RUNTIME_CLASS( class_name );

使用这个宏可以从一个c++类名,返回一个指向CRuntimeClass类结构的指针

不过这里需要注意一点:使用这个宏是有条件的。仅仅是那些从CObject继承的类,并且对该类使用了DECLARE_DYNAMIC、DECLARE_DYNCREATE或者DECLARE_SERIAL宏,允许动态生成时,才能使用这个宏。

4.AfxBeginThread函数所做的工作

当进程的主线程或其他线程调用AfxBeginThread函数来创建一个新的用户界面线程的时候,该函数做了许多的工作

1.它创建一个新的用户自己的线程类的对象,由于用户的线程类是从CWinThread类派生出来的,这个对象也继承了CWinThread类的属性。

2.MFC会自动调用新线程类的重载的函数InitInstance,来初始化这个新的线程类对象实例。这是一个必须在用户派生的线程类中的重载的函数,用户可在该函数中初始化线程,并分配任何需要的动态内存。如果初始化成功,InitInstance函数应该返回TRUE,线程就可以继续运行;如果初始化失败,如内存申请失败,就返回FALSE,线程将停止执行,并释放所拥有的资源。如果先的线程需要处理窗口,可以在InitInstance函数中创建它,可把CWinThread类的m_pMainWnd成员变量设置成指向已创建的窗口指针。如果在线程中创建了一个MFC的窗口对象,在其他的线程中是不能使用这个窗口对象的,但可以使用线程内窗口的句柄,如果用户想在一个线程中操作另一个线程的窗口对象,首先必须在该线程中创建一个新的MFC对象,然后调用Attach函数把新对象附加到另一个线程传递的窗口句柄中。

3.调用CWinThread::CreateThread函数来开始执行这个线程,最终运行CWinThread::Run函数,进入消息循环

4.函数返回一个指向新生成的CWinThread对象的指针,可以把它保存在一个变量中,其他线程就可以利用这个指针来访问该线程类的成员变量或成员函数。

系统自动的为每一个线程创建一个消息队列,如果线程创建了一个或者多个窗口,就必须提供一个消息循环,这个消息循环从线程的消息队列中获取消息,并把他们发送到相应的Windows过程。

因为系统将消息导向独立的应用程序窗口,所以,在开始线程的消息循环之前,线程必须至少创建一个窗口,大多数基于Win32操作喜用的应用程序包含一个单一的线程,该线程创建了若干窗口,一个典型的应用是为它的主窗口注册了窗口类,创建并显示这个主窗口,并且启动它的消息循环,所有这一切都在WinMain函数中。

终止线程

有两种情况会导致线程终止,一种是线程的正常终止,一种是线程的提前终止,例如,如果一个字处理器使用一个线程来进行后台打印,当打印成功的完成时,打印线程将正常终止,但是如果希望撤销打印,后台线程就必须提前终止。

1.正常终止线程

对于一个工作线程,线程运行的过程就是执行他的控制函数的过程,当控制函数的所有指令都执行完毕而返回时,线程也将终止。此线程的生命周期也就结束了。因此,实现工作线程的正常终止是很简单的,只要在执行完毕时退出控制函数,并返回一个用来表示终止原因的值即可。我们可以在工作线程的控制函数中适当的安排函数返回的出口,一般在控制函数中使用return语句返回,返回0表示线程的控制函数已成功执行完毕。

对于一个用户界面线程,一般不能直接处理线程的控制函数,CWinThread::Run成员函数是MFC为线程实现消息循环的缺省的控制函数,当这个函数收到一个WM_QUIT消息之后会终止线程,因此要正常的终止用户界面线程,应尽可能的使用消息通信的方式,只要在用户界面线程的某个事件处理函数中,调用PostQuitMessage函数,这个函数会向用户界面线程的消息队列发送一个WM_QUIT消息,Run函数收到这个消息,会自动的终止线程的运行。

2.提前终止线程

要想在线程尚未完成它的工作时提前终止线程,只需要从线程内调用AfxEndThread函数,就可以强迫线程终止

执行此函数将停止函数所在线程的执行,撤销该线程的堆栈,解除所有绑定到此线程的动态链接库,并从内存中删除此线程。特别要强调的是,此函数必须在想要终止的线程内部调用,如果想要从一个线程来终止另一个线程,就必须在两个线程之间使用通信的方法。

3.终止线程的另一种方法

使用Win32 API提供的TerminteThread函数,也可以用来终止一个正在运行的线程,但是他产生的后果是不可预料的,一般仅用来终止堆栈的死线程,此函数本身不做任何内存的清除工作。另外,使用这个方法终止的线程可能在几个不同的事务中被打断。这将导致系统处理不可预料的状态。

当然,线程是从属于进程的,如果进程因为某种原因提前终止,那么进程的所有线程也将一同终止。

4.获取线程的终止代码

当线程正常终止或者提前终止是,指定的终止代码可以被应用程序的其他线程使用,对于用户界面线程或者工作线程,要获得线程的终止代码,只需要调用GetExitCodeThread函数。

这个注意,这个函数要用到线程的句柄,线程终止时,线程就被删除了,线程的句柄还会存在么?其实,通常一个线程的句柄被包含在CWinThread类的成员变量m_hThread中,默认情况下,只要线程函数返回,或调用AfxEndThread函数,线程即被终止,相应的CWinThread对象也被删除,它的成员变量m_hThread当然就不存在了,不能在访问它,因此,如果保存一个线程的句柄就成了一个问题,获取线程的终止代码还需要采取额外的步骤,有两种方法解决。

1.可以把CWinThread对象的m_bAutoDelete变量设置为FALSE,这样,当线程终止时,就不会自动删除相应的CWinThread对象,其他线程就仍然可以访问它的m_hThread成员变量,但随之而来的问题是:应用程序框架将不会自动删除这个CWinThread对象,用户必须自己删除它。

2.另外存储线程的句柄。创建线程后,调用DuplicateHandle函数来复制m_hThread成员变量的副本到另一个变量中,并通过此变量来访问它。使用这种方法,即使线程对象已在线程终止时被自动删除,仍然可以知道该线程为什么会终止,但要注意,复制必须在线程终止之前来做。最安全的方法是,在调用AfxBeginThread函数创建并启动线程时,将他的dwCreateFlags参数设为CREATE_SUSPENDED,使线程被创建后就先挂起,然后复制线程的句柄,在调用ResumeThread函数,恢复线程的运行。

关于设置线程的优先级问题

SetThreadPriority函数设置指定线程的优先级的值,线程的优先级与线程所在的进程的优先级共同决定线程的基本优先级水平

每一个线程都有一个基本的优先级,由该线程的优先级值和它所在进程的优先级类共同决定。系统根据所有可执行线程的基本优先级来决定哪一个线程将获得下一个cpu的时间片,系统根据优先级来安排线程时候仅当没有较高优先级的可执行线程时,才安排较低优先级的线程。

MFC为我们提供的多线程到这里就讲完了。多线程在各个方面下应用广泛,尤其已服务器方面为主,总之,不管你是选择MFC的多线程也好还是选择原生态的多线程也好,用好多线程是为服务器开发准备的一块敲门砖。


« 上一篇:wifi共享上网(至尊版wifi)
« 下一篇:drcom至尊版使用openwrt路由器拨号
在这里写下您精彩的评论
  • 微信

  • QQ

  • 支付宝

返回首页
返回首页 img
返回顶部~
返回顶部 img