当前位置: 移动技术网 > IT编程>开发语言>C/C++ > MFC多线程技术

MFC多线程技术

2019年03月12日  | 移动技术网IT编程  | 我要评论

移动两城一家,钢筋表示符号,523au

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线程对象。

 

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 = 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;
}

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网