MFC多线程编程

来源:互联网 发布:无线虚能矩阵 编辑:程序博客网 时间:2024/06/10 11:42

MFC 多线程概述

MFC中有两类线程 , 分别称之为工作者线程和用户界面线程 ;
二者的主要区别在于 工作者线程没有消息循环 , 而用户界面线程有自己的消息队列和消息循环 ;

  • 工作者线程没有消息机制 , 通常用来执行后台计算和维护任务 , 如冗长的计算过程 , 打印机的后台打印等 ;
  • 用户界面线程一般用于处理独立于其他线程执行之外的用户输入 , 响应用户及系统所产生的事件和消息等 ;

但对于 Win32 的 API 编程而言 , 这两种线程是没有区别的 , 它们都只需线程的启动地址即可启动线程来执行任务 ;


MFC 多线程 API 支持

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

工作者线程

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 ;

用户界面线程

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

其中各参数说明如下 :

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

CWinThread 类

CWinThread 类的 数据成员 简要说明如下 :

`m_hThread` : 当前线程的句柄 ;  `m_nThreadID` : 当前线程的ID ;  `m_pMainWnd` : 指向应用程序主窗口的指针 ;

CWinThread 类的 常用函数 简要说明如下 :

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 ; 用户界面线程经常重载该函数 , 工作者线程一般不使用该函数函数 ;

virtual int CWinThread::ExitInstance();

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


MFC 创建多线程实例

MFC 创建工作者线程代码如下 :

新建一个 MFC 对话框项目 , 在对话框头文件定义一个工作者线程函数 , 如下 :

static UINT WorkerThreadFunc(LPVOID lpParam);

源文件实现如下 (这里让工作者线程等待一秒左右时间模拟数据处理) :

UINT CMFCWorkerThreadDlg::WorkerThreadFunc(LPVOID lpParam){    for(int nIndex=0; nIndex<100; nIndex++)    {        // Do Something        Sleep(10);    }    return 0;}

创建工作者线程代码如下 :

void CMFCWorkerThreadDlg::OnBnClickedButtonStart(){       CWinThread* pThread;    // 启动工作者线程    pThread=AfxBeginThread(WorkerThreadFunc, &Info);    // 阻塞等待工作者线程结束    WaitForSingleObject(pThread->m_hThread, INFINITE);    pThread = NULL;}

MFC 创建UI线程代码如下 :

创建 CWinThread 的派生类 CUIThread :

// UIThread.h 文件#pragma onceclass CUIThread : public CWinThread{    DECLARE_DYNCREATE(CUIThread)protected:    CUIThread();    // 动态创建所使用的受保护的构造函数    virtual ~CUIThread();public:    virtual BOOL InitInstance();    virtual int ExitInstance();protected:    DECLARE_MESSAGE_MAP()};

重载函数 InitInstance();ExitInstance(); :

// UIThread.cpp 文件#include "stdafx.h"#include "UIThread.h"IMPLEMENT_DYNCREATE(CUIThread, CWinThread)CUIThread::CUIThread(){}CUIThread::~CUIThread(){}BOOL CUIThread::InitInstance(){    CFrameWnd* pWnd=new CFrameWnd;    pWnd->Create(NULL, _T("UI Thread Window"));    pWnd->ShowWindow(SW_SHOW);    pWnd->UpdateWindow();    m_pMainWnd=pWnd;    // 这个语句必须要有    return TRUE;}int CUIThread::ExitInstance(){    return CWinThread::ExitInstance();}BEGIN_MESSAGE_MAP(CUIThread, CWinThread)END_MESSAGE_MAP()

在主对话框启动 UI 线程 :

void CMainDlg::OnBnClickedStartUI(){    CWinThread *pUIThread = AfxBeginThread(RUNTIME_CLASS(CUIThread));}

作者 Github : tojohnonly , 博客 : EnskDeCode