转载来源:http://www.cnblogs.com/txwd0033/archive/2010/12/21/1912448.html
如果要在MFC程序中产生一个线程,而该线程将调用MFC函数或者使用MFC的任何数据,那么你必须以AfxBeginThread()或者CWinThread::CreateThread()来产生这些线程,理由同C runtime library.
在MFC中启动一个worker线程
如果线程调用了GetMessage或者CreateWindow之类的函数,消息队列就会产生,而worker线程就摇身一变成了GUI线程(UI线程)。 MFC对这两种线程提供了两种不同AfxBeginThread函数。以下的函数形式是用来产生worker线程的:
所谓runtime class是MFC为了实现出RTTI和Dynamic Creation等性质,而设计的一种架构,其具体内容则为CRuntimeClass.
AfxBeginThread的UI版本,期望为你在 pThreadClass中所指定的类配置一个对象,此类必须派生之CWinThread。CWinThread提供了一堆多样化的虚函数,你可以改写以帮助消息的处理,线程的启动和清理,以及异常处理。这些虚函数有
ExitInstance |
线程终止时执行清除。通常重写。 |
InitInstance |
执行线程实例初始化。必须重写。 |
OnIdle |
执行线程特定的闲置时间处理。通常不重写。 |
PreTranslateMessage |
将消息调度到 TranslateMessage 和 DispatchMessage 之前对其进行筛选。通常不重写。 |
ProcessWndProcException |
截获由线程的消息和命令处理程序引发的未处理异常。通常不重写。 |
Run |
控制线程的函数。包含消息泵。一般不重写。 |
默认情况下只要改写InitInstance, MFC就会启动一个消息循环。
以ClassWizard产生一个UI线程的操作步骤:
我们就会发现ClassWizard 已经帮我们产生了InitInstance和ExitInstance,并且为该线程产生出了最上层的消息映射表。
我自己做了一个实验,产生一个UI线程后,然后再UI线程里DoModal一个对话框,和单线程的DoModal效果是一样的,我原来的理解是不一样的,因为他们有不同的消息循环,所以应该不会产生Domodal的效果, 为什么?
与MFC对象共处
MFC多线程有一个重大限制,会影响你所做的几乎每一件事情。MFC各对象和Win32 handles之间的映射关系记录在线程局部存储(Thread Local Storage, TLS)中,因此没有办法把一个MFC对象从某线程手上交到另一线程手上,你也不能够在线程之间传递MFC对象的指针。所谓的MFC对象包括(但不限于)CWnd , CDC, CPen,CFont,CBitmap,Cpalette。这个限制的存在阻止了“为这些对象产生同步机制“的必要性。
这个线程有几个分歧,如果2个线程都调用CWnd::GetDlgItem()以取得对话框中的一个控件(如edit),那么每个线程应该获得不同的指针--甚至即使2个线程的对象是同一个控件。如果面对一个指针,其所指对象并没有永久的MFC结构,那么当对此指针的一个索求行为出现时,MFC往往会产生出一些临时性对象。
这个限制(线程之间交换对象)的意思是说,你不能够放一个指针(指向一个CWnd)到结构中去,而该结构被一个worker线程使用。你不能够把一个指向CDialog或者CView的指针交给另一个线程。
MFC在许多地方检查”横跨线程之对象的使用情况“ 。任何时候,只要MFC对着对象调用ASSERT_VALID,它便会检查对象是否保持在线程局部存储(TLS)中。
线程局部存储(TLS)的使用说明了以AfxBeginThread在MFC程序中产生UI线程的重要性,如果你用的是_beginthreadex()或者CreateThread时,MFC不会给你机会产生用以维护其handles的必要结构。
在线程之间共享对象的解决方案。不要放置MFC对象,改放对象的handle。你可以利用GetSafeHandle获得派生自CGdiObject的对象的handle。这样的对象包括CPen和Cpalette对象。还可以利用GetSafeHwnd 获得派生自CWnd的对象的handle,如CDialog对象。当你把handle传递给新线程时,线程可以把该handle附着到一个新的MFC对象,使用FromHandle可以产生一个临时对象,使用Attach则可以产生一个永久对象。而在退出之前,线程应该调用Detach。如果线程只是想短暂的使用这个数值,可以产生一个临时对象 如:CDC *pDC = CDC::FromHandle(hOriginalDC).
另外一个替代方案就是送出一个用户自定义的消息,要来通信。
MFC的同步控制
一下摘自MSDN
MFC 提供的多线程类分为两类:同步对象(CSyncObject、CSemaphore、CMutex、CCriticalSection 和 CEvent)和同步访问对象(CMultiLock 和 CSingleLock)。
若要确定应使用的同步类,请询问以下一系列问题:
应用程序必须等到发生某事才能访问资源(例如,在将数据写入文件之前,必须先从通信端口接收它)吗?
如果是,请使用 CEvent。
同一应用程序内一个以上的线程可以同时访问此资源(例如,应用程序允许在同一文档上最多同时打开五个带有视图的窗口)吗?
如果是,请使用 CSemaphore。
可以有一个以上的应用程序使用此资源(例如,资源在 DLL 中)吗?
如果是,请使用 CMutex。
如果不是,请使用 CCriticalSection。
从不直接使用 CSyncObject。它是其他四个同步类的基类。
一个关于多线程的博文