《Win32多线程程序设计》学习笔记 第10章 MFC 中的线程

摘要:
CreateThread()用于生成这些线程:原因与Criminalibrary相同。在MFC中启动工作线程。如果线程调用GetMessage或CreateWindow等函数,则使用以下函数形式生成工作线程。他的句柄也关闭以存储线程的句柄和ID。此函数可以防止CWinThread对象被自动删除。如果设置了对象,则无法自动删除;

如果要在MFC程序中产生一个线程,而该线程将调用MFC函数或者使用MFC的任何数据,那么你必须以AfxBeginThread()或者CWinThread::CreateThread()来产生这些线程,理由同C runtime library.

在MFC中启动一个worker线程

如果线程调用了GetMessage或者CreateWindow之类的函数,消息队列就会产生,而worker线程就摇身一变成了GUI线程(UI线程)。 MFC对这两种线程提供了两种不同AfxBeginThread函数。以下的函数形式是用来产生worker线程的:

CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,  
//函数名称,用来启动线程
   LPVOID pParam,        //任意4字节数值,用来传给新线程,可以是整数或者指针
   int nPriority = THREAD_PRIORITY_NORMAL,   //线程优先级
   UINT nStackSize = 0,
   DWORD dwCreateFlags 
= 0,     
   LPSECURITY_ATTRIBUTES lpSecurityAttrs 
= NULL  //安全属性
);

返回值:如果失败,返回NULL,否则传回一个指向新创建的CWinThread对象的指针。

AfxBeginThread传回的是一个指针,而不是Handle。AfxBeginThread做了很多后台的工作,不用我们担心何时关闭handle。他通过以下方式来办到

清楚CWinThread对象。默认情况下,线程结束时,CWinThread对象会自动被删除。这是由于MFC安插了自己的线程启动函数。

关闭线程的handle。用以删除CWinThread对象的那个清理函数,同时也关闭了线程的handle。当线程结束生命时,他的handle也就被关闭了

储存线程的handle和ID。分别是CWinThread的m_hThread 和 m_nThreadID 成员变量。

 安全的使用AfxBeginThread()的返回值

 如果从线程启动到结束的时间很短,CWinThread对象可能在AfxBeginThread返回时就已经被删除掉了。在这种情况下,任何触及线程的handle的操作,都会让程序挂掉。CWinThread中有一个成员变量m_bAutoDelete,这个函数可以阻止CWinThread对象被自动删除。为了能够设定此变量不产生一个race condition,你必须先以挂起状态产生线程。

CWinThread * newThread = AfxBeginThread( ThreadFunc, (LPVOID) i, THREAD_PRIORITY_NORMAL, 0
          CREATE_SUSPENDED );
newThread
->m_bAutoDelete = FALSE;
newThread
->ResumeThread();

如果你设定了对象是他不能自动删除,你就得自己删除之, delete newThread; 那么CWinThread的析构函数会自动的调用CloseHandle关闭线程的handle。

AfxBeginThread函数在内部实际上做了一下几个服务:

  1. 在heap中配置一个新的CWinThread对象
  2. 调用CWinThread::CreateThread并设定属性,使线程以挂起状态产生。
  3. 设定线程的优先权。
  4. 调用CWinThread::ResumeThread();

 我们可以藉由此机制,自己扩展CWinThread类。

在MFC中启动一个UI线程

 启动一个UI线程的AfxBeginThread函数版本如下

CWinThread* AfxBeginThread(
   CRuntimeClass
* pThreadClass,     //指向你所产生的一个类的runtime class,该类派生自CWinThread
   int nPriority = THREAD_PRIORITY_NORMAL,   //线程优先权
   UINT nStackSize = 0,         
   DWORD dwCreateFlags 
= 0,      //是否挂起
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL  //安全属性
);
 
返回值同第一种形式

所谓runtime class是MFC为了实现出RTTI和Dynamic Creation等性质,而设计的一种架构,其具体内容则为CRuntimeClass.

 AfxBeginThread的UI版本,期望为你在 pThreadClass中所指定的类配置一个对象,此类必须派生之CWinThread。CWinThread提供了一堆多样化的虚函数,你可以改写以帮助消息的处理,线程的启动和清理,以及异常处理。这些虚函数有

ExitInstance

线程终止时执行清除。通常重写。

InitInstance

执行线程实例初始化。必须重写。

OnIdle

执行线程特定的闲置时间处理。通常不重写。

PreTranslateMessage

将消息调度到 TranslateMessageDispatchMessage 之前对其进行筛选。通常不重写。

ProcessWndProcException

截获由线程的消息和命令处理程序引发的未处理异常。通常不重写。

Run

控制线程的函数。包含消息泵。一般不重写。

 默认情况下只要改写InitInstance, MFC就会启动一个消息循环。

以ClassWizard产生一个UI线程的操作步骤:

  1.  添加一个MFC类给你的项目中
  2. 将MFC类的Base Class选择为CWinThread, 然后Create。

 我们就会发现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)。

 若要确定应使用的同步类,请询问以下一系列问题:

  1. 应用程序必须等到发生某事才能访问资源(例如,在将数据写入文件之前,必须先从通信端口接收它)吗?

    如果是,请使用 CEvent

  2. 同一应用程序内一个以上的线程可以同时访问此资源(例如,应用程序允许在同一文档上最多同时打开五个带有视图的窗口)吗?

    如果是,请使用 CSemaphore

  3. 可以有一个以上的应用程序使用此资源(例如,资源在 DLL 中)吗?

    如果是,请使用 CMutex

    如果不是,请使用 CCriticalSection

从不直接使用 CSyncObject。它是其他四个同步类的基类。

 一个关于多线程的博文

http://www.cnblogs.com/zhouhuayu/archive/2005/09/22/242196.html

免责声明:文章转载自《《Win32多线程程序设计》学习笔记 第10章 MFC 中的线程》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇(已解决)Could not open '/var/lib/nova/mnt/*/volume-*': Permission deniedShell 命令行解析 getopt工具下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

jQuery同步Ajax带来的UI线程阻塞问题及解决办法

俗话说不作死就不会死,今天作死了一回,写了一个比较二逼的函数,遇到了同步Ajax引起的UI线程阻塞问题,在此记录一下。   事情起因是这样的,因为页面上有多个相似的异步请求动作,本着提高代码可重用性的原则,我封装了一个名为getData的函数,它接收不同参数,只负责获取数据,然后把数据return。基本的逻辑剥离出来是这样的: function get...

AE加载不同数据的方法(GeoDatabase空间数据管理)

先看一下GeoDatabase核心结构模型图:     1  工作空间工厂WorkspaceFactory对象    WorkspaceFactory是GeoDatabase的入口,是一个抽象类,拥有很多子类,例如SdeWorkspaceFactory, AccessWorkspaceFactory, ShapfileWorkspaceFactory ...

反序列化漏洞

反序列化 原理介绍 序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象。Java中的ObjectOutputStream类的writeObject()方法可以实现序列化,类ObjectInputStream类的readObject()方法用于反序列化。比如你可以将字符串对象先进行序列化,存储到本地文件,然后再...

GDB常用调试命令以及多进程多线程调试

http://blog.csdn.net/freeelinux/article/details/53700266 一:普通命令   1.list命令 list  linenum      显示程序第linenum行周围的程序 list  function      显示函数名为function的函数的源程序 list                  ...

Java多线程学习之任务的创建以及在线程中执行任务

传统的创建任务、驱动任务的方式 1.继承Thread类   通过继承Thead类,并重写run方法,在run方法里面编码具体的任务,调用对象的start方法驱动任务。    public class TestThread extends Thread{ private int count = 5;   //创建介绍String形参的构造器,一般...

java 基本理论知识点

http://www.cnblogs.com/hellokitty1/p/4491808.html 1、main方法是怎么写的        public static void main(String [] args){}//最习惯的      public static void main(String  args[]){}      static p...