Fluray 发表于 2014-5-4 21:40:24

【C++】多线程全盘文件搜索

本帖最后由 Fluray 于 2014-5-5 08:23 编辑

新建一个控制台,要支持MFC。
只用了MFC一个函数AfxBeginThread,其余的全是win API。线程函数修改自《windows程序设计 第2版》 王艳平 张铮 编著
// 多线程文件搜索.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <afxwin.h>
#include <process.h>
#include <iostream>
#include <stddef.h>

#define BUFSIZE 1024
using namespace std;
//int g_FileName=0;
struct CDirectoryNode : public CNoTrackObject
{
    CDirectoryNode* pNext;// CTypedSimpleList类模板要使用此成员
    char szDir;   // 要查找的目录
};

class CRapidFinder
{
public:
    CRapidFinder(int nMaxThread);
    virtual ~CRapidFinder();
    BOOL CheckFile(LPCTSTR lpszFileName);

    int m_nResultCount;                           // 结果数目
    int m_nThreadCount;                           // 活动线程数目
    CTypedSimpleList<CDirectoryNode*> m_listDir;    // 目录列表
    CRITICAL_SECTION m_cs;                        // 关键代码段

    const int m_nMaxThread;               // 最大线程数目
    char m_szMatchName;         // 要搜索的文件
    HANDLE m_hDirEvent;                     // 向m_listDir中添加新的目录后置位(受信)
    HANDLE m_hExitEvent;                  // 各搜索线程将要退出时置位(受信)
};
// m_nMaxThread成员是一个const类型的变量,必须使用成员初始化列表来初始化它的值
CRapidFinder::CRapidFinder(int nMaxThread) : m_nMaxThread(nMaxThread)
{
    m_nResultCount = 0;
    m_nThreadCount = 0;
    m_szMatchName = '\0';

    m_listDir.Construct(offsetof(CDirectoryNode, pNext));
    m_hDirEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    m_hExitEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    ::InitializeCriticalSection(&m_cs);
}

CRapidFinder::~CRapidFinder()
{
    ::CloseHandle(m_hDirEvent);
    ::CloseHandle(m_hExitEvent);
    ::DeleteCriticalSection(&m_cs);
}
//包含此文件名的文件都会被查找到
//BOOL CRapidFinder::CheckFile(LPCTSTR lpszFileName)
//{
//char string;
//char strSearch;
//strcpy(string, lpszFileName);
//strcpy(strSearch, m_szMatchName);
//
//// 将字符串string和strSearch中的字符全部转化成大写
//_strupr(string);
//_strupr(strSearch);
//
//// 找出字符串strSearch在字符串string中第一次出现的位置
//// 如果string中不包含strSearch,strstr函数返回NULL
//if(strstr(string, strSearch) != NULL)
//      return TRUE;
//
//return FALSE;
//}
//必须是相同问文件名(大小写可以不同)
BOOL CRapidFinder::CheckFile(LPCTSTR lpszFileName)
{
    char string;
    char strSearch;
    strcpy(string, lpszFileName);
    strcpy(strSearch, m_szMatchName);

    // 将字符串string和strSearch中的字符全部转化成大写
    _strupr(string);
    _strupr(strSearch);

    //如果两个文件名称相同
    if(strcmp(string, strSearch) == 0)
      return TRUE;

    return FALSE;
}
UINT FinderEntry(LPVOID lpParam)
{
    CRapidFinder* pFinder = (CRapidFinder*)lpParam;
    CDirectoryNode* pNode = NULL;   // 从m_listDir中取出的节点
    BOOL bActive = TRUE;      // 指示当前线程的状态

    // 循环处理m_listDir列表中的目录
    while(1)
    {
      // 从m_listDir列表中取出一个新的目录
         ::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);

      // m_listDir为空的话就试图在m_hDirEvent事件上等待
      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);
            pFinder->m_nThreadCount++;
            ::LeaveCriticalSection(&pFinder->m_cs);
            bActive = TRUE;
            continue;
      }
            
      
      // 在pNode指向的目录中查找文件

      WIN32_FIND_DATA fileData;
      HANDLE hFindFile;
      // 设置成X:\XXXX\*.*的格式
      if(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)
                {
                  // 将搜索到的目录添加到目录列表中

                  // 为新的节点申请内存空间,设置完整的目录名称
                  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);
                        char FileName;
                  inti;
                   for (i=strlen(pNode->szDir)-4;i>=0;i--)
                        FileName=pNode->szDir;
                FileName='\0';
                strcat(FileName, fileData.cFileName);
                printf("%s\n",FileName);
                  
                  }      
                }
            }while(::FindNextFile(hFindFile, &fileData));
      }

      // 此节点保存的目录已经搜索完毕,释放内存空间,进入下次循环
      delete pNode;
      pNode = NULL;
    }

    // 促使一个搜索线程从WaitForSingleObject函数返回,并退出循环
    ::SetEvent(pFinder->m_hDirEvent);

    // 判断此线程是否是最后一个结束循环的线程,如果是就通知主线程
    if(::WaitForSingleObject(pFinder->m_hDirEvent, 0) != WAIT_TIMEOUT)
    // 如果此时pFinder->m_hDirEvent所对应的事件对象为受信状态,
    // WaitForSingleObject函数的返回值将是WAIT_OBJECT_0
    {
      // 通知主线程最后一个搜索线程即将退出,文件搜索完毕
      ::SetEvent(pFinder->m_hExitEvent);
   
    }
    return 0;
}


void main()
{

    char szFile;
    char szPath ;
    char   szLogicalDriveStrings;
    DWORD   iLength;
    int iSub;
    int count=0;
    clock_t start,end;//程序运行的起始和结束时间
    float costtime;//程序耗时
    printf("请输入想查找的文件名:");
    scanf("%s",szFile);
    ZeroMemory(szLogicalDriveStrings, BUFSIZE);
    iLength = GetLogicalDriveStringsA(BUFSIZE-1, szLogicalDriveStrings);
    start=clock();//开始计时
    for(iSub=0; iSub<iLength; iSub+=4)
    {
    CRapidFinder* pFinder = new CRapidFinder(64);
    CDirectoryNode* pNode = new CDirectoryNode;
    //如果不是固定磁盘驱动器:本地硬盘或移动硬盘,忽略
      if(GetDriveType(szLogicalDriveStrings+iSub)!=3)
            continue;
      /*printf("%s", szLogicalDriveStrings+iSub);*/
    // 设置参数信息
    strcpy(pNode->szDir, szLogicalDriveStrings+iSub);
    pFinder->m_listDir.AddHead(pNode);
    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("%s 中最终查找到的文件的个数为:%d \n",szLogicalDriveStrings+iSub, pFinder->m_nResultCount);*/
    count=count+pFinder->m_nResultCount;
    delete pFinder;
    }
    end=clock();//计时结束
    costtime= (float)(end - start) / CLOCKS_PER_SEC;//转换时间格式
    /*ShellExecute(NULL,"open",g_FileName,NULL,NULL,SW_SHOWNORMAL);*/
    printf("使用64个线程全盘搜索文件%s,一共搜索到%d个,用时%.5f s",szFile,count,costtime);
    system("pause");
}

邪丶帝 发表于 2014-5-12 13:30:05

楼主 这个只支持 W7?还是通用??

fsjaky 发表于 2014-7-7 12:54:08

谢谢楼主分享 正好在写这方面的程序

samson987 发表于 2014-7-30 16:19:59

感謝大大的分享

曦。风起 发表于 2017-2-21 19:01:34

学习了 谢谢分享

tx7790 发表于 2017-2-22 10:47:35

感谢楼主分享
页: [1]
查看完整版本: 【C++】多线程全盘文件搜索