⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mfc中文教程(二).htm

📁 visual c++ MFC教程式
💻 HTM
📖 第 1 页 / 共 5 页
字号:
        <P 
        align=justify>如果一段程序代码需要对某个资源进行同步保护,则这是一段关键段代码。在进入该关键段代码前调用EnterCriticalSection函数,这样,其他线程都不能执行该段代码,若它们试图执行就会被阻塞。</P>
        <P 
        align=justify>完成关键段的执行之后,调用LeaveCriticalSection函数,其他的线程就可以继续执行该段代码。如果该函数不被调用,则其他线程将无限期的等待。</P>
        <P align=justify></P>
        <LI>事件对象 
        <P></P>
        <P 
        align=justify>首先,调用CreateEvent函数创建一个事件对象,该函数返回一个事件句柄。然后,可以设置(SetEvent)或者复位(ResetEvent)一个事件对象,也可以发一个事件脉冲(PlusEvent),即设置一个事件对象,然后复位它。复位有两种形式:自动复位和人工复位。在创建事件对象时指定复位形式。。</P>
        <P 
        align=justify>自动复位:当对象获得信号后,就释放下一个可用线程(优先级别最高的线程;如果优先级别相同,则等待队列中的第一个线程被释放)。</P>
        <P align=justify>人工复位:当对象获得信号后,就释放所有可利用线程。</P>
        <P align=justify>最后,使用CloseHandle销毁创建的事件对象。</P>
        <P align=justify></P>
        <LI>互斥对象 
        <P></P>
        <P 
        align=justify>首先,调用CreateMutex创建互斥对象;然后,调用等待函数,可以的话利用关键资源;最后,调用RealseMutex释放互斥对象。</P>
        <P align=justify>互斥对象可以在进程间使用,但关键段对象只能用于同一进程的线程之间。</P>
        <P align=justify></P>
        <LI>信号量对象 
        <P></P>
        <P align=justify>在Win32中,信号量的数值变为0时给以信号。在有多个资源需要管理时可以使用信号量对象。</P>
        <P 
        align=justify>首先,调用CreateSemaphore创建一个信号量;然后,调用等待函数,如果允许的话,则利用关键资源;最后,调用RealeaseSemaphore释放信号量对象。</P>
        <P align=justify></P>
        <LI>此外,还有其他句柄可以用来同步线程: 
        <P></P></LI></OL>
      <P align=justify>文件句柄(FILE HANDLES)</P>
      <P align=justify>命名管道句柄(NAMED PIPE HANDELS)</P>
      <P align=justify>控制台输入缓冲区句柄(CONSOLE INPUT BUFFER HANDLES)</P>
      <P align=justify>通讯设备句柄(COMMUNICTION DEVICE HANDLES)</P>
      <P align=justify>进程句柄(PROCESS HANDLES)</P>
      <P align=justify>线程句柄(THREAD HANDLES)</P>
      <P align=justify>例如,当一个进程或线程结束时,进程或线程句柄获得信号,等待该进程或者线程结束的线程被释放。</P>
      <OL>
        <OL>
          <OL>
            <P align=justify>
            <LI><A name=_Toc445889087></A><A name=_Toc445782490></A><A 
            name=_Toc452640951></A><A name=_Toc457299049></A><B>等待函数</B> 
            <P></P></LI></OL></OL></OL>
      <P align=justify>Win32提供了一组等待函数用来让一个线程阻塞自己的执行。等待函数分三类:</P>
      <OL>
        <P align=justify>
        <LI>等待单个对象的(FOR SINGLE OBJECT): 
        <P></P>
        <P align=justify>这类函数包括:</P>
        <P align=justify>SignalObjectAndWait</P>
        <P align=justify>WaitForSingleObject</P>
        <P align=justify>WaitForSingleObjectEx</P>
        <P align=justify>函数参数包括同步对象的句柄和等待时间等。</P>
        <P align=justify>在以下情况下等待函数返回:</P>
        <P align=justify>同步对象获得信号时返回;</P>
        <P 
        align=justify>等待时间达到了返回:如果等待时间不限制(Infinite),则只有同步对象获得信号才返回;如果等待时间为0,则在测试了同步对象的状态之后马上返回。</P>
        <P align=justify></P>
        <LI>等待多个对象的(FOR MULTIPLE OBJECTS) 
        <P></P>
        <P align=justify>这类函数包括:</P>
        <P align=justify>WaitForMultipleObjects</P>
        <P align=justify>WaitForMultipleObjectsEx</P>
        <P align=justify>MsgWaitForMultipleObjects</P>
        <P align=justify>MsgWaitForMultipleObjectsEx</P>
        <P align=justify>函数参数包括同步对象的句柄,等待时间,是等待一个还是多个同步对象等等。</P>
        <P align=justify>在以下情况下等待函数返回:</P>
        <P align=justify>一个或全部同步对象获得信号时返回(在参数中指定是等待一个或多个同步对象);</P>
        <P 
        align=justify>等待时间达到了返回:如果等待时间不限制(Infinite),则只有同步对象获得信号才返回;如果等待时间为0,则在测试了同步对象的状态之后马上返回。</P>
        <P align=justify></P>
        <LI>可以发出提示的函数(ALTERABLE) 
        <P></P></LI></OL>
      <P align=justify>这类函数包括:</P>
      <P align=justify>MsgWaitForMultipleObjectsEx</P>
      <P align=justify>SignalObjectAndWait</P>
      <P align=justify>WaitForMultipleObjectsEx</P>
      <P align=justify>WaitForSingleObjectEx</P>
      <P align=justify>这些函数主要用于重叠(Overlapped)的I/O(异步I/O)。</P>
      <OL>
        <OL>
          <P align=justify>
          <LI><A name=_Toc445889088></A><A name=_Toc445782491></A><A 
          name=_Toc452640952></A><A name=_Toc457299050></A><B>MFC的线程处理</B> 
          <P></P>
          <P align=justify>在Win32 
          API的基础之上,MFC提供了处理线程的类和函数。处理线程的类是CWinThread,函数是AfxBeginThread、AfxEndThread等。</P>
          <P align=justify>表5-6解释了CWinThread的成员变量和函数。</P>
          <P 
          align=justify>CWinThread是MFC线程类,它的成员变量m_hThread和m_hThreadID是对应的Win32线程句柄和线程ID。</P>
          <P align=justify>MFC明确区分两种线程:用户界面线程(User interface 
          thread)和工作者线程(Worker 
          thread)。用户界面线程一般用于处理用户输入并对用户产生的事件和消息作出应答。工作者线程用于完成不要求用户输入的任务,如耗时计算。</P>
          <P align=justify>Win32 
          API并不区分线程类型,它只需要知道线程的开始地址以便它开始执行线程。MFC为用户界面线程特别地提供消息泵来处理用户界面的事件。CWinApp对象是用户界面线程对象的一个例子,CWinApp从类CWinThread派生并处理用户产生的事件和消息。</P>
          <OL>
            <P align=justify>
            <LI><A name=_Toc445889089></A><A name=_Toc445782492></A><A 
            name=_Toc452640953></A><A name=_Toc457299051></A><B>创建用户界面线程</B> 
            <P></P></LI></OL></LI></OL></OL>
      <P align=justify>通过以下步骤创建一个用户界面线程:</P>
      <UL>
        <P align=justify>
        <LI>从CWinThread派生一个有动态创建能力的类。使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏来支持动态创建。 

        <P></P>
        <P align=justify></P>
        <LI>覆盖CWinThread的一些虚拟函数,可以覆盖的函数见表5-4关于CWinThread的部分。其中,函数InitInstance是必须覆盖的,ExitInstance通常是要覆盖的。 

        <P></P>
        <P align=justify></P>
        <LI>使用AfxBeginThread创建MFC线程对象和Win32线程对象。如果创建线程时没有指定CREATE_SUSPENDED,则开始执行线程。 

        <P></P>
        <P align=justify></P>
        <LI>如果创建线程是指定了CREATE_SUSPENDED,则在适当的地方调用函数ResumeThread开始执行线程。 
        <P></P></LI></UL>
      <OL>
        <OL>
          <OL>
            <P align=justify>
            <LI><A name=_Toc445889090></A><A name=_Toc445782493></A><A 
            name=_Toc452640954></A><A name=_Toc457299052></A><B>创建工作者线程</B> 
            <P></P>
            <P 
align=justify>程序员不必从CWinThread派生新的线程类,只需要提供一个控制函数,由线程启动后执行该函数。</P>
            <P 
            align=justify>然后,使用AfxBeginThread创建MFC线程对象和Win32线程对象。如果创建线程时没有指定CREATE_SUSPENDED(创建后挂起),则创建的新线程开始执行。</P>
            <P 
            align=justify>如果创建线程是指定了CREATE_SUSPENDED,则在适当的地方调用函数ResumeThread开始执行线程。</P>
            <P 
            align=justify>虽然程序员没有从CWinThread派生类,但是MFC给工作者线程提供了缺省的CWinThread对象。</P>
            <P align=justify></P>
            <LI><A name=_Toc445889091></A><A name=_Toc445782494></A><A 
            name=_Toc452640955></A><A 
            name=_Toc457299053></A><B>AfxBeginThread</B> 
            <P></P></LI></OL></OL></OL>
      <P 
      align=justify>用户界面线程和工作者线程都是由AfxBeginThread创建的。现在,考察该函数:MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程:</P>
      <OL>
        <P align=justify>
        <LI>用户界面线程的AfxBeginThread 
        <P></P>
        <P align=justify>用户界面线程的AfxBeginThread的原型如下:</P>
        <P align=justify>CWinThread* AFXAPI AfxBeginThread(</P>
        <P align=justify>CRuntimeClass* pThreadClass,</P>
        <P align=justify>int nPriority, </P>
        <P align=justify>UINT nStackSize, </P>
        <P align=justify>DWORD dwCreateFlags,</P>
        <P align=justify>LPSECURITY_ATTRIBUTES lpSecurityAttrs)</P>
        <P align=justify>其中:</P>
        <P align=justify>参数1是从CWinThread派生的RUNTIME_CLASS类;</P>
        <P align=justify>参数2指定线程优先级,如果为0,则与创建该线程的线程相同;</P>
        <P align=justify>参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;</P>
        <P 
        align=justify>参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。</P>
        <P align=justify>参数5表示线程的安全属性,NT下有用。</P>
        <P align=justify></P>
        <LI>工作者线程的AfxBeginThread 
        <P></P>
        <P align=justify>工作者线程的AfxBeginThread的原型如下:</P>
        <P align=justify>CWinThread* AFXAPI AfxBeginThread(</P>
        <P align=justify>AFX_THREADPROC pfnThreadProc, </P>
        <P align=justify>LPVOID pParam,</P>
        <P align=justify>int nPriority, </P>
        <P align=justify>UINT nStackSize, </P>
        <P align=justify>DWORD dwCreateFlags,</P>
        <P align=justify>LPSECURITY_ATTRIBUTES lpSecurityAttrs)</P>
        <P align=justify>其中:</P>
        <P align=justify>参数1指定控制函数的地址;</P>
        <P align=justify>参数2指定传递给控制函数的参数;</P>
        <P align=justify>参数3、4、5分别指定线程的优先级、堆栈大小、创建标识、安全属性,含义同用户界面线程。</P>
        <P align=justify></P>
        <LI>AfxBeginThread创建线程的流程 
        <P></P></LI></OL>
      <P 
      align=justify>不论哪个AfxBeginThread,首先都是创建MFC线程对象,然后创建Win32线程对象。在创建MFC线程对象时,用户界面线程和工作者线程的创建分别调用了不同的构造函数。用户界面线程是从CWinThread派生的,所以,要先调用派生类的缺省构造函数,然后调用CWinThread的缺省构造函数。图8-1中两个构造函数所调用的CommonConstruct是MFC内部使用的成员函数。</P><IMG 
      height=468 hspace=12 src="MFC中文教程(二).files/20050611022034928.gif" 
      width=349 align=left> 
      <OL>
        <OL>
          <OL>
            <P align=justify>
            <LI><A name=_Toc445889092></A><A name=_Toc445782495></A><A 
            name=_Toc452640956></A><A 
            name=_Toc457299054></A><B>CreateThread和_AfxThreadEntry</B> 
            <P></P>
            <P 
            align=justify>MFC使用CWinThread::CreateThread创建线程,不论对工作者线程或用户界面线程,都指定线程的入口函数是_AfxThreadEntry。_AfxThreadEntry调用AfxInitThread初始化线程。</P>
            <P 
            align=justify>CreateThread和_AfxThreadEntry在线程的创建过程中使用同步手段交互等待、执行。CreateThread由创建线程执行,_AfxThreadEntry由被创建的线程执行,两者通过两个事件对象(hEvent和hEvent2)同步:</P>
            <P 
            align=justify>在创建了新线程之后,创建线程将在hEvent事件上无限等待直到新线程给出创建结果;新线程在创建成功或者失败之后,触发事件hEvent让父线程运行,并且在hEven2上无限等待直到父线程退出CreateThread函数;父线程(创建线程)因为hEvent的置位结束等待,继续执行,退出CreateThread之前触发hEvent2事件;新线程(子线程)因为hEvent2的置位结束等待,开始执行控制函数(工作者线程)或者进入消息循环(用户界面线程)。</P>
            <P align=justify>MFC在线程创建中使用了如下数据结构:</P>
            <P align=justify>struct _AFX_THREAD_STARTUP</P>
            <P align=justify>{</P>
            <P align=justify>//传递给线程启动的参数(IN)</P>
            <P align=justify>_AFX_THREAD_STATE* pThreadState;//父线程的线程状态</P>
            <P align=justify>CWinThread* pThread; //新创建的MFC线程对象</P>
            <P align=justify>DWORD dwCreateFlags; //线程创建标识</P>
            <P align=justify>_PNH pfnNewHandler; //新线程的句柄</P>
            <P align=justify>HANDLE hEvent; //同步事件,线程创建成功或失败后置位</P>
            <P align=justify>HANDLE hEvent2; //同步事件,新线程恢复执行后置位</P>
            <P align=justify></P>
            <P align=justify>//返回给创建线程的参数,在新线程恢复执行后赋值</P>
            <P align=justify>BOOL bError; //如果创建发生错误,TRUE</P>
            <P align=justify>};</P>
            <P 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -