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

和完成例程比较。完成端口的效率更高。其主要原因是完成端口可以指定线程池。这么说吧,完成例程,每次只能由一个线程来监控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 //---------------------------------------------------------------------------
原文地址:https://www.cnblogs.com/songr/p/4179298.html