欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MFC多线程技术

程序员文章站 2022-04-08 11:50:52
MFC中有两类线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。 工作者线程没笑消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程之外的用户输入,响应用户及 ......

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

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

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

(1)工作者线程

cwndthread *afxbeginthread(afx_threadproc pfnthreadproc,
    lpvoid pparam,
    uint npriority=thread_priority_normal,
    uint nstacksize = 0,
    dword dwcreateflags = 0,
    lpsecurity_attributes lpsecurityattrs = null);

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

cwndthread *afxbeginthread(cruntimeclass *pthreadclass,
    int npriority=thread_priority_normal,
    uint nstacksize = 0,
    dword dwcreateflags = 0,
lpsecurity_attributes lpsecurityattrs = null);

afxbeginthread()创建线程的流程不论哪个afxbeginthread(),首先都是创建mfc线程对象,然后创建win32线程对象。

 MFC多线程技术

afxbeginthread创建线程的流程图

 

 

mfc线程技术剖析

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

 

                           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多线程技术

mfc状态类的层次

 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 = 0;
    m_nthreadcount = 0;
    m_szmatchname[0] = '\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(1)
    {
        // 取出新目录 互斥的取待查目录
        ::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 == 0)
            {
                ::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)-1] != '\')
            strcat(pnode->szdir,'\\');
        strcat(pnode->szdir,"*.*");
        hfindfile = ::findfirstfile(pnode->szdir,&filedata);
        
        if(hfindfile != invalid_handle_value)
        {
            do
            {
                if(filedata.cfilename[0] == '.')
                    continue;
                if(filedata.dwfileattributes & file_attribute_directory)
                { // 是目录,添加到m_listdir
                    cdirectorynode *p = new cdirectorynode;
                    strncpy(p->szdir,pnode->szdir,strlen(pnode->szdir)-3);
                    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,0) != wait_timeout)
    { // 通知主线程,最后一个搜索线程已经结束了
        ::setevent(pfinder->m_hexitevent);
    }
    
    return 0;
}

int main(void)
{
    crapidfinder *pfinder = new crapidfinder(64); // 开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 =0; i < pfinder->m_nmaxthread; i++)
    {
        afxbeginthread(finderentry,pfinder);
    }
    waitforsingleobject(pfinder->m_hexitevent,infinite);
    // 打印查找结果
    printf("一共找到同名文件%d个\n");
    
    delete pfinder;
    
    system("pause");
    return 0;
}