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

工作者线程没笑消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程之外的用户输入,响应用户及系统产生的事件和消息等。但对于Win32的API编程而言,这两种编程是没有区别的,他们都只需要线程的启动地址即可启动线程来执行任务。

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

(1)工作者线程

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

(2)IU线程(用户界面线程)

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

AfxBeginThread()创建线程的流程不论哪个AfxBeginThread(),首先都是创建MFC线程对象,然后创建Win32线程对象。

AfxBeginThread创建线程的流程图

MFC线程技术剖析

MFC的核心类库中有一个名为CWinThread的类,这个类在MFC的底层机理中占举足轻重的地位。

                           MFC应用程序     

线程状态用类_AFX_THREAD_STATE描述,模块状态用类_AFX_MODULE_STATE描述,模块-线程状态用类_AFX_MODULE_THREAD_STATE描述。这些类从类CNoTrackObject派生。进程状态用类_AFX_BASE_MODULE_STATE描述,从模块状态_AFX_MODULE_STATE派生。进程状态是一个可以独立执行的MFC应用程序的模块状态。还有其他状态如DLL的模块状态等也从模块状态类_AFX_MODULE_STATE派生。

MFC状态类的层次

模块、线程、模块-线程状态的关系

多线程实践案例:(多线程文件查找器)

查找文件的时候,首先用FindFirstFile函数,如果函数执行成功,返回句柄hFindFile来对应这个寻找操作,接下来可以利用这个句柄循环调用FindNextFile函数继续查找其他文件,知道该函数返回失败(FALSE)为止。最后还要调用FindClose函数关闭hFindFile句柄。

hFindFile = ::FindFirstFile(lpFileName,lpFindData);

if(hFindFile != INVALID_HANDLE_VALUE)
{
do // 处理本次找到的文件
{ }while(::FindNextFile(lpFileName,lpFindData));
::FindColse(hFindFile);
}

文件搜索器要在指定的目录及所有子层目录中查找文件,然后向用户显示出查找的结果。如果使用多线程的话,就意味着各线程要同时在不同目录中搜索文件。

这个程序最关键的地方是定义了一个动态的目录列表。

CTypedSimpleList<CDirectoryNode *> m_listDir;
struct CDirectoryNode : public CNoTrackObject
{
CDirectoryNode* pNext; // CTypedSimpleList类模板要用次成员
char szDir[MAX_PATH]; // 要查找的目录
}

在线程执行查找文件任务的时候,如果找到的是目录就将它添加到列表中,若找到的是文件,就用自定义CheckFile函数进行比较,判断是否符合查找条件,若符合就打印出来,显示给用户。线程在查找完一个目录以后,再从m_listDir列表中取出一个新的目录进行查找,同时将该目录对应的结点从表中删除。

当m_listDir为空时,线程就要进入暂停状态,等待其他线程向m_listDir中添加新的目录。

案例:

RapidFile.h文件

#pragma once
#include <afxwin.h> struct CDirectoryNode : public CNoTrackObject // 创建文件夹目录结构体
{
CDirectoryNode *pNext; // 文件夹目录的下一个指针
char szDir[MAX_PATH]; // 文件夹名称
} class CRapidFinder
{
public:
CRapidFinder(int nMaxThread); // 构造函数
virtual ~CRapidFinder(); // 虚析构函数 BOOL CheckFile(LPCTSTR lpszFileName); // 匹配文件夹名字 int m_nResultCount; // 结果的数量
int m_nThreadCount; // 活动线程的数量 CTypeSimpleList<CDirectoryNode *> m_listDir; // 文件夹列表
CRITICAL_SECTION m_cs; // 临界区 const int m_nMaxThread; // 最大线程数量
char m_szMatchName[MAX_PATH]; // 最大搜索的文件 // 通知线程的工作状态
HANDLE m_hDirEvent; //我们向m_listDir添加新的目录,10个线程 9个停止,1个工作 若m_listDir为空,线程不能停止
HANDLE m_hExitEvent; // 各个搜索线程是否已经结束
}

RapidFile.cpp文件

#include "RapidFile"
#include <string> CRapidFinder::CRapidFinder(int nMaxThread) : m_nMaxThread(nMaxThread)
{
m_nResultCount = ;
m_nThreadCount = ;
m_szMatchName[] = '\0'; m_listDir.Construct(offsetof(CDirectoryNode,pNext)); // 创建CTypedSimpleList
m_hDirEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
m_hExitEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
::InitializeCriticalSectioin(&m_cs);
} CRapidFinder::~CRapidFinder()
{
::CloseHandle(m_hDirEvent);
::CloseHandle(m_hExitEvent);
::DeleteCriticalSection(&m_cs);
} // 查找文件名
BOOL CRapidFinder::CheckFile(LPCTSTR lpszFileName)
{
char str[MAX_PATH];
char strSearch[MAX_PATH];
strcpy(str,lpszFileName);
strcpy(strSearch,m_szMatchName); _strupr(str); // 将字符串全部转换为大写
_strupr(strSearch); if(strstr(str,strSearch) != NULL) // 查找的文件名在里面
{
return TRUE;
}
return FALSE;
}

MultiThreadFindFile.cpp

#include <stdio.h>
#include <afxwin.h>
#include "RapidFile.h" UINT FinderEntry(LPVOID lpParam)
{
CRapidFinder *pFinder = (CRapidFinder *)lpParam;
CDirectoryNode *pNode = NULL; // m_listDir从pNode中获取
BOOL bActive = TRUE; // 线程状态 // 只要m_listDir有目录
while()
{
// 取出新目录 互斥的取待查目录
::EnterCriticalSection(&pFinder->m_cs);
if(pFinder->m_listDir.IsEmpty())
bActive = FALSE;
else
{
pNode = pFinder->m_listDir.GetHead();
pFinder->m_listDir.Remove(pNode);
}
::LeaveCriticalSection(&pFinder->m_cs); // bActive指示了当前线程的工作状态,如果m_listDir队列当前为空,那么我们当前线程先等待
if(!bActive)
{
::EnterCriticalSection(&pFinder->m_cs);
pFinder->m_nThreadCount--;
if(pFinder->m_nThreadCount == )
{
::LeaveCriticalSection(&pFinder->m_cs);
break;
}
::LeaveCriticalSection(&pFinder->m_cs); // 进入等待状态
ResetEvent(pFinder>m_hDirEvent);
::WaitForSingleObject(pFinder->m_hDirEvent,INFINITE); ::EnterCriticalSection(&pFinder->m_cs);
// 此时当前线程再度获得CPU的推进机会
pFinder->m_nThreadCount++; // 当前的活动线程数量加1
::LeaveCriticalSection(&pFinder->m_cs); bActive = TRUE;
continue;
} // 实现基于pNode的目录查找
WIN32_FIND_DATA fileData;
HANDLE hFindFile;
if(pNode->szDir[strlen(pNode->szDir)-] != '\')
strcat(pNode->szDir,'\\');
strcat(pNode->szDir,"*.*");
hFindFile = ::FindFirstFile(pNode->szDir,&fileData); if(hFindFile != INVALID_HANDLE_VALUE)
{
do
{
if(fileData.cFileName[] == '.')
continue;
if(fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{ // 是目录,添加到m_listDir
CDirectoryNode *p = new CDirectoryNode;
strncpy(p->szDir,pNode->szDir,strlen(pNode->szDir)-);
strcat(p->szDir,fileData.cFileName); ::EnterCriticalSection(&pFinder->m_cs);
pFinder->m_listDir.AddHead(p);
::LeaveCriticalSection(&pFinder->m_cs);
// 置信一个事件
::SetEvent(pFinder->m_hDirEvent);
}
else // 文件
{
if(pFinder->CheckFile(fileData.cFileName)) // 找到文件名
{
::EnterCriticalSection(&pFinder->m_cs);
::InterlockedIncrement((long *)&pFinder->m_nResultCount);
::LeaveCriticalSection(&pFinder->m_cs);
printf("%s \n",fileData.cFileName);
}
}
}while(::FindNextFile(pNode->szDir,&fileData));
}
// 此节点的保存的目录已经全部搜索完毕
delete pNode;
pNode = NULL;
}
::SetEvent(pFinder->m_hExitEvent);
// 判断当前线程是否是最后一个结束循环的线程
if(::WaitForSingleObject(pFinder->m_hDirEvent,) != WAIT_TIMEOUT)
{ // 通知主线程,最后一个搜索线程已经结束了
::SetEvent(pFinder->m_hExitEvent);
} return ;
} int main(void)
{
CRapidFinder *pFinder = new CRapidFinder(); // 开64个线程
CDirectoryNode *pNode = new CDirectoryNode; // 创建结点 char szPath[] = "C:\\"; // 需要查找的目录
char szFile[] = "stdafx"; // 需要查找的字符串 // 对CRapider的信息进行设置
strcpy(pNode->szDir,szPath); // 设置要搜索的目录
pFinder->m_listDir.AddHead(pNode); // 将要搜索的目录添加到list中,当做头结点
strcpy(pFinder->m_szMatchName,szFile); // 需要搜索的文件名 // 创建辅助线程
pFinder->m_nThreadCount = pFinder->m_nMaxThread; // 创建辅助线程,并等待查找结束
for(int i =; i < pFinder->m_nMaxThread; i++)
{
AfxBeginThread(FinderEntry,pFinder);
}
WaitForSingleObject(pFinder->m_hExitEvent,INFINITE);
// 打印查找结果
printf("一共找到同名文件%d个\n"); delete pFinder; system("pause");
return ;
}

MFC多线程技术的更多相关文章

  1. MFC 框架技术简单研讨

    引用:http://www.cnblogs.com/chinazhangjie/archive/2011/09/20/2181986.html 正文: 第一讲 Win32 App  和  MFC Fr ...

  2. VC中利用多线程技术实现线程之间的通信

    当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力.用进程和线程的观点来研究软 ...

  3. MFC多线程各种线程用法 .

    http://blog.csdn.net/qq61394323/article/details/9328301 一.问题的提出 编写一个耗时的单线程程序: 新建一个基于对话框的应用程序SingleTh ...

  4. MFC多线程

    当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力.用进程和线程的观点来研究软 ...

  5. MFC多线程详细讲解(转)

    一.问题的提出 编写一个耗时的单线程程序: 新建一个基于对话框的应用程序SingleThread,在主对话框IDD_SINGLETHREAD_DIALOG添加一个按钮,ID为IDC_SLEEP_SIX ...

  6. iOS多线程技术方案

    iOS多线程技术方案 目录 一.多线程简介 1.多线程的由来 2.耗时操作的模拟试验 3.进程和线程 4.多线程的概念及原理 5.多线程的优缺点和一个Tip 6.主线程 7.技术方案 二.Pthrea ...

  7. C#多线程技术总结(异步)

    我这里针对现有的C#多线程技术进行一个汇总,一是复习,二是方便索引,文章部份知识点来源于网络,非本人原创. 一.并行(异步): 1.System.Threading.Tasks命名空间下的(TPL): ...

  8. iOS开发之多线程技术

    本篇争取一篇讲清讲透,依然将通过四大方面清晰的对iOS开发中多线程的用法进行详尽的讲解: 一.什么是多线程 1)多线程执行原理 2)线程与进程 3)多线程的优缺点 二.我们为什么要用多线程编程技术 三 ...

  9. iOS多线程技术

    iOS多线程技术主要分配NSThread.NSOperation和GCD.下边来简单的介绍一下吧. 随性一点,就不按照顺序来了.所以先介绍一下NSOperation. ---------------- ...

随机推荐

  1. seo工具

    http://tool.seowhy.com/ 一.关键词查词类工具:可以查询出更多目标客户可能搜索的词语 1.百度指数:http://index.baidu.com/ 这个工具是使用人数最多的 2. ...

  2. c++中如何定义编译期间常量,即这个常量可以用于定义数组下标

    在c++中,类里面的成员变量不仅仅可以被const修饰,还可以被static const修饰,此时一个内建类型(如int ,char ,long等)的static const 可以看做是一个编译期间的 ...

  3. 在delphi XE5 里面编译kbmmw4.3

    Delphi XE5 仓促的发布了,虽然开始支持Android 开发了,但是经过试用,发现那个模拟器慢到无法用, 真机可以运行,但是调试也几乎无法用.由于XE5 的主要增加的是Android 的开发支 ...

  4. 做SEO都需要具备哪些方面的知识

    做seo需要了解的基本知识有利于seo工作的进行 一.了解搜索引擎的工作原理 搜索引擎的基本工作原理包括如下三个过程: 1.首先在互联网中发现.搜集网页信息; 2.同时对信息进行提取和组织建立索引库; ...

  5. 2018.10.08 NOIP模拟 序列(主席树)

    传送门 T2防ak题? 其实也不是很难(考试时sb了). 直接变形一下求出区间长度在[l2,r2][l2,r2][l2,r2]之间,中位数≤l1−1\le l1-1≤l1−1的区间数,和区间长度在[l ...

  6. EF生成的SQL语句执行顺序问题。

    //实体被更改后,再做删除,EF只生成删除语句 //实体删除后再更改,EF报错 //添加语句会再,更改,删除后执行,更AddObject位置无关 //一个实体多个字段被改,只会生成一句update / ...

  7. UVa 11367 Full Tank? (DP + Dijkstra)

    题意:n个城市有m条道路.每个城市的油价不一样,给出起点s和终点t,以及汽车的油箱的容量,求从城市s到城市 t 的最便宜路径. 析:dp[u][i] 表示在第 u 个城市,还剩下 i L升油,一开始用 ...

  8. MATLAB常用函数

      Matlab的内部常数 pi                   圆周率 exp(1)             自然对数的底数e i 或j                虚数单位 Inf或 inf ...

  9. Spring3.x错误---- Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

    Spring3.x错误: 解决方法: 缺少cglib的包, 下载地址: http://sourceforge.net/projects/cglib/files/latest/download?sour ...

  10. 20170908工作日记--UML画类图、HTTP协议、Volley源码走读

    随手搜了一下,Android studio居然能够自动帮追我们生成UML的类图,简直太棒了http://www.gcssloop.com/course/UsePlantUMLInAS(Win),具体做 ...