和完成例程比较。完成端口的效率更高。其主要原因是完成端口可以指定线程池。这么说吧,完成例程,每次只能由一个线程来监控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 //---------------------------------------------------------------------------