zoukankan      html  css  js  c++  java
  • (VC/MFC)多线程(MultiThreading) 1. 基本概念.

    在Win32环境中,每个运行的应用程序都建立一个进程(Process),每个进程有一个或多个执行线程(Thread)组成.

    MFC把执行的线程封装在CWinThread类中,它还包括了同步类,这些类封装了事件,互斥,和可在Windows核心中找到的其他线程同步对象。

    MFC区分了两种不同类型的线程: 用户界面线程(user-interface thread)工作者线程(worker thread).
    两者的主要区别在于user-interface thread有消息循环,而工作者线程没有。
    user-interface thread 可以创建窗口和处理发送给这些窗口的消息。worker thread执行后台任务,这些后台任务不直接接受用户的输入,因此不需要窗口和消息循环。

    user-interface thread 最常用的是创建多窗口,这些窗口有分开的执行线程来负责管理。
    worker thread 适合于执行孤立的任务,这些任务能够与应用程序的其他部分相脱离,并且当其他处理在前台发生时它在后台执行。


    创建worker thread
    -法1)构造一个CWinThread对象,并调用该对象的CreateThread()函数来创建线程。
    -法2)用AfxBeginThread()构造一个CWinThread对象并同时创建一个线程。
      ; 注意,不要使用Win32的::CreteThread()函数在MFG程序中创建线程。Win32的这个API 和上述的 CWinThread::CreateThread(),AfxBeginThread()有所不同。
    上述的2种方法除了启动线程外,还初始化被框架使用的内部变量,在线程创建过程期间的各个点上执行安全性检查,并采取步骤保用一个线程安全的访问C运行库中的函数。

    //启动一个worker thread, 并传给它一个应用程序定义的数据结构ThreadInfo的地址。
    CwinThread *pThread=AfxBeginThread(ThreadFunc,&ThreadInfo);

    //ThreadFunc 是线程函数,当线程开始执行时,该函数得以执行。
    UNIT ThreadFunc(LPVOID pParam){
     UNIT nlterations=(UNIT)pParam;
     for(UNIT i=0;i<nlterations;i++)
      return 0;
    }

    AfxBeginThread()的原型如下:
    CWinThread* AfxBeginThread(
     AFX_THREADPROC pfnThreadProc,                     //线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam ),不能设置为NULL;
     LPVOID pParam,                                                         //传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
     int nPriority=THREAD_PRIORITY_NORMAL,          //该线程的执行优先级。
     UNIT nStackSize=0,                                                    //该线程的最大堆栈尺寸。
     DWORD dwCreateFlags=0,                                     //默认值0,告诉系统立即开始执行该线程。
     LPSCURITY_ATTRIBUTES lpSecurityAttrs=NULL         
    );

    创建user-interface 线程.
    - 与创建worker thread 的过程不同,一个worker thread是由它的线程函数定义,但是一个user-interface thread是由一个动态可创建的类来控制,
      该类是从CWinThread派生的。
    - 一个CUIThread是通过调用AfxBeginThread()来启动的,该函数接受指向该线程类的一个CRuntimeClass指针。
      CWinThread *pThread=AfxBeginThread(RUNTIME_CLASS(CUIThread));


    AfxBeginThread() 创建线程详解 

    在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。两种重载函数原型和参数分别说明如下: 

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

    PfnThreadProc:指向工作者线程的执行函数的指针,线程函数原型必须声明如下: UINT ExecutingFunction(LPVOID pParam);

    请注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。 

    pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略; 

    nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级; 

    nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果nStackSize被设为0,则线程的堆栈被设置成与父线程堆栈相同大小; 

    dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起; 

    lpSecurityAttrs:线程的安全属性指针,一般为NULL; 
     (2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,
                          int nPriority=THREAD_PRIORITY_NORMAL,
                          UINT nStackSize=0,
                          DWORD dwCreateFlags=0,
                          LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);


      pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同形式1。使用函数的这个原型生成的线程也有消息机制,在以后的例子中我们将发现同主线程的机制几乎一样。

    下面我们对CWinThread类的数据成员及常用函数进行简要说明。 

       m_hThread:当前线程的句柄; 
       m_nThreadID:当前线程的ID; 
       m_pMainWnd:指向应用程序主窗口的指针 
       BOOL CWinThread::CreateThread(DWORD dwCreateFlags=0,
       UINT nStackSize=0,
       LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

      该函数中的dwCreateFlags、nStackSize、lpSecurityAttrs参数和API函数CreateThread中的对应参数有相同含义,该函数执行成功,返回非0值,否则返回0。
      一般情况下,调用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。 

    virtual BOOL CWinThread::InitInstance();

      重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不使用InitInstance()。 
    virtual int CWinThread::ExitInstance();

      在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同InitInstance()成员函数一样,该函数也只适用于用户界面线程。 


     


    挂起和继续执行线程
    -挂起: CWinThread::SuspendThread()
    -继续: CWinthread::ResumeThread()

    使线程睡眠.
    -通过调用API函数 ::Sleep() , 例如 ::Sleep(1000)使自己睡眠。

    终止一个线程:
    -对于worker thread , 当一个worker thread的线程函数执行一个返回语句或者调用AfxEndThread()时,这个worker thread 要终止。
    -对于user-interface thread, 当一个WM_QUIT消息被发送到它的消息队列中,或该线程中的一个函数调用AfxEndThread()时,该线程就被终止。
      ; 一个线程可以用API函数 ::PostQuitMessage()把一个WM_QUIT消息发送到自身程序上.
    - AfxEndThrea()和 ::PostQuitMessage()返回一个32位的退出吗,在该线程被终止后,可以用GetExitCodeThread()来检索改码。
     DWORD dwExitCode;
     ::GetExitCodeThread(pThread->m_hThread,&dwExitCode);
       如果调用一个仍在执行的线程,则::GetExitcodeThread()把dwExitCode置为STILL_ACTIVE(0x103).

    从一个线程终止另一个线程:
    -一般来说,线程只能自我终止,如果想要线程A终止线程B,必须建立一个信号通知机制,运行线程A告诉线程B终止它自己。
    //Thread A
    static BOOL bContinue=TRUE;
    CWinThread *pThread=AfxBeginThread(ThreadFunc,&bContinue);

    //Do some other work

    //Save the thread handle.
    HANDLE hThrend=pThread-<m_hThread;

    //Tell thread B to terminate
    bContinue=FALSE;

    //::WaitForSingleObject()一直处于等待状态,知道被指定的对象(另一个线程)输入一个信号为止
    //当线程终止时,一个线程对象将由无信号变成有信号的。
    //第一个参数:想要等待的对象的句柄。第二个参数:愿意等待的时间长度。
    ::WaitForSingleObject(hThread,INFINITE);

    //Thread B
    UINT ThreadFunc(LPVOID pParam){
     BOOL *pContinue=(BOOL *)pParam;
     while(*pContinue){
     // Do some work
     }
     return 0;
    }

    要结束线程的两种方式

      1 : 这是最简单的方式,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束,

      当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线

      程的一切资源都会被回收.

      2 : 如果你想让另一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息.

      不管是工作者线程还是界面线程,如果你想在线程结束后得到它的结果,那么你可以调用:

      ::GetExitCodeThread函数

  • 相关阅读:
    Linux进程理解与实践(四)wait函数处理僵尸进程
    Linux进程理解与实践(三)进程终止函数和exec函数族的使用
    system V信号量和Posix信号量
    Linux进程间通信方式--信号,管道,消息队列,信号量,共享内存
    linux 高并发socket通信模型
    信号集函数
    进程间通信使用信号
    使用消息队列
    改变域名,php
    php函数
  • 原文地址:https://www.cnblogs.com/fdyang/p/2858732.html
Copyright © 2011-2022 走看看