文件查找之 模拟everything (一)

来源:互联网 发布:合肥市行知学校在哪 编辑:程序博客网 时间:2024/06/14 07:32

文件搜索是我们编码的时候经常遇到的问题,如何提高查找性能和效率是我们的目标,本文总结了我在实现文件查找性能提升时做的一些方法

常规实现: 递归查找  -- 通过传递根路径,递归查找

代码:

 void MyFindFile(wstring strPath, wstring strFileName)
{
    WIN32_FIND_DATA findData;
    HANDLE hFile;
    hFile = FindFirstFile((strPath + L"\\*.*").c_str(), &findData);
    wstring  wstrPathTmp;
    do
    {
        if (!wcscmp(findData.cFileName, L".") || !wcscmp(findData.cFileName, L".."))
        {
            continue;
        }
        if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            wstrPathTmp = strPath + L"\\" + findData.cFileName;
            MyFindFile(wstrPathTmp.c_str(), strFileName);
        }
        if (wcsstr(findData.cFileName, strFileName.c_str()))
        {
            wcout << strPath + L"\\" + findData.cFileName << endl;
        }
    } while (FindNextFile(hFile, &findData));
}


尝试2:通过多线程提高性能,每次搜索如果查找到文件就进行目标匹配,否则就创建一个新的线程对新目标进行搜索,线程函数代码如下:

unsigned int __stdcall ThreadFun1(PVOID param){


    pThreadInfo info = new ThreadInfo();
    info = (pThreadInfo)param;
    wstring strPath = info->strPath;
    wstring strFindName = info->strFileName;
    WIN32_FIND_DATA findData;
    HANDLE hFile;
    hFile = FindFirstFile((strPath + L"\\*.*").c_str(), &findData);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        //过滤掉没有权限或者其他原因不能范围的文件夹或者文件
        g_TotalThreadNum--;
        if (g_TotalThreadNum == 0)
        {
            SetEvent(g_event);
        }
        CloseHandle(hFile);
        return 0;
    }
    wstring  wstrPathTmp;
    do
    {
        if (!wcscmp(findData.cFileName, L".") || !wcscmp(findData.cFileName, L".."))
        {
            continue;
        }
        if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            g_TotalFolderNum++;
            wstrPathTmp = strPath + L"\\" + findData.cFileName;
            pThreadInfo param = new ThreadInfo();
            param->strFileName = strFindName;
            param->strPath = wstrPathTmp;
            g_TotalThreadNum++;
            HANDLE hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, param, NULL, NULL);
            CloseHandle(hThreadHandle);
        }
        else
        {
            g_TotalFileNum++;
            if (wcsstr(findData.cFileName, strFindName.c_str()))
            {
                g_FindFileNum++;
                wstring strFileInfo = strPath + L"\\" + findData.cFileName;
                wcout << strFileInfo << endl;
            }
        }
    } while (FindNextFile(hFile, &findData));
    //释放申请的资源
    delete param;
    param = NULL;
    g_TotalThreadNum--;
    if (g_TotalThreadNum == 0)
    {
        SetEvent(g_event);
    }
    return 0;
}

但是我在主函数中想分析上述代码的性能,将执行的时间打印出来,发现退出事件g_event 不会有信号,经过多次调试发现这是一个“”坑“”代码,由于我搜索的是C盘整个目录

而我们的电脑硬件不能同时创建那么多个线程,会出现创建线程失败的情况,所以上述策略不好,而且我们在_beginthreadex 创建线程的时候一定要对返回值做判断。

尝试3: 创建指定个数的线程(一般的策略是 cpu核心数*2) ,这样就不会出现上面的问题

线程函数代码如下:

unsigned int __stdcall ThreadFun2(PVOID param)
{
    while (true)
    {
        WaitForSingleObject(hGolobalEvent, INFINITE);
        wstring strCurPath;
        wstring srtCurName;
        HANDLE hExitEvent = INVALID_HANDLE_VALUE;
        int iCurThreadId = GetCurrentThreadId();
        map<int, HANDLE>::iterator iter = hExitEvents.find(iCurThreadId);
        if (iter != hExitEvents.end())
        {
            hExitEvent  = iter->second;
        }
        EnterCriticalSection(&g_cs);
        if (strTotalFolders.size() != 0)
        {
            pPoolThreadInfo tmpInfo = strTotalFolders.back();
            strCurPath = tmpInfo->strFindPath;
            srtCurName = tmpInfo->strFindFileName;
            strTotalFolders.pop_back();
            ResetEvent(hExitEvent);
        }
        else
        {
            ResetEvent(hGolobalEvent);
        }
        LeaveCriticalSection(&g_cs);
        if (!strCurPath.empty())
        {
            WIN32_FIND_DATA findData;
            HANDLE hFile;
            hFile = FindFirstFile((strCurPath + L"\\*.*").c_str(), &findData);
            if (hFile == INVALID_HANDLE_VALUE)
            {
                //过滤掉没有权限或者其他原因不能范围的文件夹或者文件
                SetEvent(hExitEvent);
                continue;
            }
            do
            {
                wstring  wstrPathTmp;
                if (!wcscmp(findData.cFileName, L".") || !wcscmp(findData.cFileName, L".."))
                {
                    continue;
                }
                if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
                    g_TotalFolderNum++;
                    wstrPathTmp = strCurPath + L"\\" + findData.cFileName;
                    pPoolThreadInfo newInfo = new PoolThreadInfo();
                    newInfo->strFilter = L"*.*";
                    newInfo->strFindPath = wstrPathTmp;
                    newInfo->strFindFileName = L"ntdll";
                    EnterCriticalSection(&g_cs);
                    strTotalFolders.push_back(newInfo);
                    LeaveCriticalSection(&g_cs);
                    SetEvent(hGolobalEvent);
                 
                }
                else
                {
                    g_TotalFileNum++;
                    if (wcsstr(findData.cFileName, srtCurName.c_str()))
                    {
                        g_FindFileNum++;
                        wstring strFileInfo = strCurPath + L"\\" + findData.cFileName;
                        EnterCriticalSection(&g_OutPutCs);
                        wcout << strFileInfo << endl;
                        LeaveCriticalSection(&g_OutPutCs);
                    }
                }
            } while (FindNextFile(hFile, &findData));
            //本次搜索结束,发出退出信号
            SetEvent(hExitEvent);
        }
        else
        {
            SetEvent(hExitEvent);
            //ResetEvent(hGolobalEvent);
            continue;
        }
    }
}

经过比较性能较递归性能一般情况下提高不少,但是相较于evertthing还是要慢,后面经过查资料,发现everthing的原理是提前搜索将数据存入数据库,后面的查找都是查找数据库操作,后面我会实现模拟everything;

下面我将编码过程中遇到的坑总结如下:

1: 要记得判断是否创建成功,尤其是在创建多个线程的情况下,这个是一个坑,当然其他的系统api也要注意同样的问题

2:记得释放句柄资源,释放new 创建的对象

3:做文件IO操作 或者其他访问的时候,要注意可能存在读取权限的问题,比如有些文件不让你读,拒绝访问

4:创建线程最好用_beginTreadex 不要使用createThread  --这方面的论述比较多

5:要充分考虑线程同步访问的问题,使用STL的容器要考虑STL是多线程不安全的,还有cout输出也是要做多线程同步






原创粉丝点击