• 使用完成端口监控文件目录的例子


    和完成例程比较。完成端口的效率更高。其主要原因是完成端口可以指定线程池。这么说吧,完成例程,每次只能由一个线程来监控IO 完成变化。但是完成端口,却可以指定

    一堆线程(想象一下一个人干活和一群人干活的区别)轮班的监控,即1号线程忙碌了,2号线程顶上去,如果2号也忙,3号顶上去!这样,效率大大提高。

    但是呢,线程的创建和销毁是非常消耗时间的。所以,使用了线程池。另外线程涉及到CPU 上下文的切换问题。所以多线程的数量一般选择2*CPU数量,但这是个经验值

    实际情况需要反复测试。

    下面是BCB实现效果及源码(参考资料 windows 核心编程 第10章,11章)

    2014/12/23更新,增加临界值保证cout的输出不出现乱码

    2014/12/23更新,将ReadDirectoryChangesW 的缓冲区,移动到多线程中执行。

      1 //---------------------------------------------------------------------------
      2 
      3 #include <vcl.h>
      4 #include <iostream>
      5 using namespace std;
      6 #pragma hdrstop
      7 
      8 //---------------------------------------------------------------------------
      9 
     10 #pragma argsused
     11 
     12 HANDLE hMonitorDir = NULL;
     13 HANDLE hCompletePort = NULL;
     14 //因为cout 不是线程安全的,所以要用临界值
     15 CRITICAL_SECTION cs;
     16 const DWORD CompleteKey = 1;
     17 //让完成端口处理线程池自己决定最多的线程并发数
     18 const DWORD MaxThreadCount = 0;
     19 DWORD dwByteReturn;
     20 OVERLAPPED overlapped={0};
     21 DWORD dwError = 0;
     22 int fileCount = 0;
     23 
     24 
     25 //线程池
     26 DWORD WINAPI ThreadProc(LPVOID lpParameter)
     27 {
     28     DWORD dwTranferBytes = 0;
     29     DWORD dwKey = 0;
     30     LPOVERLAPPED pOverLapped = &overlapped;
     31     //捕获文件信息的缓冲长度
     32     DWORD dwBufLen
     33         =2*(sizeof(FILE_NOTIFY_INFORMATION)+MAX_PATH*sizeof(TCHAR));
     34     //捕获文件信息的缓冲buffer
     35     FILE_NOTIFY_INFORMATION* Buffer
     36         =(FILE_NOTIFY_INFORMATION*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dwBufLen);
     37 
     38     //开始监控文件夹
     39     if(!ReadDirectoryChangesW(hMonitorDir,Buffer,dwBufLen,TRUE,
     40             FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME,
     41             &dwByteReturn,&overlapped,NULL))
     42     {
     43         dwError = GetLastError();
     44         CloseHandle(hMonitorDir);
     45         HeapFree(GetProcessHeap(),0,Buffer);
     46         cout<<"监控文件夹失败! "<<SysErrorMessage(dwError).c_str()<<endl;
     47         return 0;
     48     }
     49     
     50     while(true)
     51     {
     52 
     53         //完成端口的关键所在
     54         //和完成例程相比,完成端口的优势是可以用线程池来监控IO,效率更高
     55         BOOL blOk = GetQueuedCompletionStatus(hCompletePort,
     56                                 &dwTranferBytes,
     57                                 &dwKey,
     58                                 &pOverLapped,INFINITE);
     59 
     60         dwError = GetLastError();
     61 
     62         if(blOk && CompleteKey == dwKey)
     63         {
     64             //输出线程编号
     65             EnterCriticalSection( &cs );
     66             cout<<"线程号:"<<GetCurrentThreadId()<<endl;
     67             LeaveCriticalSection( &cs );
     68             
     69             //捕获到文件变化
     70             FILE_NOTIFY_INFORMATION* notify = Buffer;
     71 
     72             AnsiString fileName =
     73                     WideCharLenToString(
     74                         notify->FileName,notify->FileNameLength/2);
     75             do
     76             {
     77                 bool blNormal = true;
     78                 switch(notify->Action)
     79                 {
     80                 case FILE_ACTION_ADDED:
     81                     {
     82                         EnterCriticalSection( &cs );
     83                         cout<<"增加了文件"<<fileName.c_str()<<endl;
     84                         LeaveCriticalSection( &cs );
     85                     }
     86                     break;
     87                 case FILE_ACTION_REMOVED:
     88                     {
     89                         EnterCriticalSection( &cs );
     90                         cout<<"删除了文件"<<fileName.c_str()<<endl;
     91                         LeaveCriticalSection( &cs );
     92                     }
     93                     break;
     94                 case FILE_ACTION_MODIFIED:
     95                     {
     96                         EnterCriticalSection( &cs );
     97                         cout<<"修改了文件"<<fileName.c_str()<<endl;
     98                         LeaveCriticalSection( &cs );
     99                     }
    100                     break;
    101                 case FILE_ACTION_RENAMED_OLD_NAME:
    102                     {
    103                         EnterCriticalSection( &cs );
    104                         cout<<"被重名的文件"<<fileName.c_str()<<endl;
    105                         LeaveCriticalSection( &cs );
    106                     }
    107                     break;
    108                 case FILE_ACTION_RENAMED_NEW_NAME:
    109                     {
    110                         EnterCriticalSection( &cs );
    111                         cout<<"新命名的文件"<<fileName.c_str()<<endl;
    112                         LeaveCriticalSection( &cs );
    113                     }
    114                     break;
    115                 default:   //有可能已经溢出了!
    116                     blNormal = false;
    117                     break;
    118                 }
    119 
    120                 if(!blNormal)
    121                 {
    122                     break;
    123                 }
    124                 //将指针偏移offset个字节
    125                 notify =  notify + notify->NextEntryOffset;
    126             }
    127             while(notify->NextEntryOffset>0);
    128 
    129             if(!ReadDirectoryChangesW(hMonitorDir,Buffer,dwBufLen,TRUE,
    130                     FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME ,
    131                     &dwByteReturn,&overlapped,NULL))
    132             {
    133                 dwError = GetLastError();
    134                 CloseHandle(hMonitorDir);
    135                 CloseHandle(hCompletePort);
    136                 HeapFree(GetProcessHeap(),0,Buffer);
    137                 cout<<"监控文件夹失败! "<<SysErrorMessage(dwError).c_str()<<endl;
    138                 break;
    139             }
    140         }
    141         else
    142         {
    143             cout<<"完成端口异常! "<<SysErrorMessage(dwError).c_str()<<endl;
    144             CloseHandle(hMonitorDir);
    145             CloseHandle(hCompletePort);
    146             HeapFree(GetProcessHeap(),0,Buffer);
    147             break;
    148         }
    149     }
    150 
    151     return 0;
    152 }
    153 
    154 int main(int argc, char* argv[])
    155 {
    156     cout<<"开始监控程序目录!...."<<endl;
    157     AnsiString fileName = ExtractFileDir(Application->ExeName);
    158 
    159     //打开目录
    160     hMonitorDir=CreateFile(fileName.c_str(),
    161         FILE_LIST_DIRECTORY,  //表明打开一个目录
    162         FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
    163         NULL,
    164         OPEN_EXISTING,
    165         FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED,//FILE_FLAG_OVERLAPPED表示异步模式
    166         NULL);
    167     dwError = GetLastError();
    168     if (INVALID_HANDLE_VALUE == hMonitorDir)
    169     {
    170         cout<<"打开文件目录失败! "<<SysErrorMessage(dwError).c_str()<<endl;
    171         return 0;
    172     }
    173     //创建完成端口
    174     hCompletePort =
    175         CreateIoCompletionPort(hMonitorDir,NULL,CompleteKey,MaxThreadCount);
    176     dwError = GetLastError();
    177     if(NULL == hCompletePort)
    178     {
    179         cout<<"创建完成端口失败! "<<SysErrorMessage(dwError).c_str()<<endl;
    180         CloseHandle(hMonitorDir);
    181         return 0;
    182     }
    183 
    184     InitializeCriticalSection( &cs );
    185     //启动线程池,在线程池中进行IO 完成监控
    186     //如果是VISTA 以上级别的操作系统,建议使用
    187     //TrySubmitThreadpoolCallback
    188     //这里,我启用五个工作线程,但实际上完成端口会自己调剂
    189     for(int i = 0; i<5; i++)
    190     {
    191         QueueUserWorkItem(ThreadProc,NULL,0x00000000|0x00000010);
    192     }
    193 
    194 
    195 
    196     int i = 0;
    197     cin>>i;
    198     CloseHandle(hMonitorDir);
    199     CloseHandle(hCompletePort);
    200     return 0;
    201 }
    202 //---------------------------------------------------------------------------
  • 相关阅读:
    检测Linux硬盘IO数据
    获取OrangePI板子CPU温度
    ASP.Net开发WebAPI跨域访问(CORS)的精简流程
    一些常用复合命令
    关于Linux的虚拟内存管理
    Linux中组 与 用户的管理
    linux加载与使用ko驱动
    7z命令行 极限压缩指令
    nodejs的POST请求
    案例:用ajax 方法 解析xml
  • 原文地址:https://www.cnblogs.com/songr/p/4179298.html
Copyright © 2020-2023  润新知