今天兴冲冲开始琢磨Windows2008新线程池中的IOCP部分新增的API的调用,也就是新的完成端口的调用方式,新的API如下:
CancelThreadpoolIo
CloseThreadpoolIo
CreateThreadpoolIo
StartThreadpoolIo
WaitForThreadpoolIoCallbacks
这几个函数其实从名字并不难理解其调用方法,于是乎我就直接开始新建项目,调用它们做实验,首先就是创建一个可以IOCP方式操作的文件对象,然后就利用CreateThreadpoolIo创建了一个线程池,并与之前的文件绑定,调用StartThreadpoolIo启动IOCP线程池,最后用CloseThreadpoolIo关闭线程池,整个代码如下(代码看起来很乱,因为网易博客的格式排版拍代码简直是灾难,各位请粘贴回VC再看吧):
#define _WIN32_WINNT 0x0600
#include
#include
#include
#define GRS_ALLOC(sz) HeapAlloc(GetProcessHeap(),0,sz)
#define GRS_CALLOC(sz) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz)
#define GRS_SAFEFREE(p) if(NULL != p){HeapFree(GetProcessHeap(),0,p);p=NULL;}
#define GRS_USEPRINTF() TCHAR pBuf[1024] = {}
#define GRS_PRINTF(...) \
StringCchPrintf(pBuf,1024,__VA_ARGS__);\
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),pBuf,lstrlen(pBuf),NULL,NULL);
#define GRS_ASSERT(s) if(!(s)) {::DebugBreak();}
VOID GetAppPath(LPTSTR pszBuffer)
{
DWORD dwLen = 0;
if(0 == (dwLen = ::GetModuleFileName(NULL,pszBuffer,MAX_PATH)))
{
return;
}
DWORD i = dwLen;
for(; i > 0; i -- )
{
if( '\\' == pszBuffer[i] )
{
pszBuffer[i + 1] = '\0';
break;
}
}
}
#define GRS_BEGINTHREAD(Fun,Param) CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Fun,Param,0,NULL)
#define GRS_MAXWRITEPERTHREAD 100 //每个线程最大写入次数
#define GRS_MAXWTHREAD 20 //写入线程数量
#define GRS_OP_READ 0x1 //读取操作
#define GRS_OP_WRITE 0x2 //写入操作
#define GRS_OP_EXIT 0x3 //关闭IOCP操作
struct ST_MY_OVERLAPPED
{
OVERLAPPED m_ol; //Overlapped 结构,不一定非得是第一个成员
HANDLE m_hFile; //操作的文件句柄
DWORD m_dwOp; //操作类型GRS_OP_READ/GRS_OP_WRITE
LPVOID m_pData; //操作的数据
UINT m_nLen; //操作的数据长度
DWORD m_dwWrite; //写入字节数
DWORD m_dwTimestamp; //起始操作的时间戳
};
//IOCP线程池回调函数,实际就是完成通知响应函数
VOID CALLBACK IoCompletionCallback(PTP_CALLBACK_INSTANCE Instance,PVOID Context,PVOID Overlapped
,ULONG IoResult,ULONG_PTR NumberOfBytesTransferred,PTP_IO Io);
//写文件的线程
DWORD WINAPI WThread(LPVOID lpParameter);
//当前操作的文件对象的指针
LARGE_INTEGER g_liFilePointer = {};
//IOCP线程池
PTP_IO g_pThreadpoolIO = NULL;
int _tmain()
{
GRS_USEPRINTF();
TCHAR pFileName[MAX_PATH] = {};
GetAppPath(pFileName);
StringCchCat(pFileName,MAX_PATH,_T("IOCPFile.txt"));
HANDLE ahWThread[GRS_MAXWTHREAD] = {};
DWORD dwWrited = 0;
//创建文件
HANDLE hTxtFile = CreateFile(pFileName
,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
if(INVALID_HANDLE_VALUE == hTxtFile)
{
GRS_PRINTF(_T("CreateFile(%s)失败,错误码:0x%08x\n")
,pFileName,GetLastError());
_tsystem(_T("PAUSE"));
return 0;
}
//初始化线程池环境数据结构
TP_CALLBACK_ENVIRON PoolEnv = {};
InitializeThreadpoolEnvironment(&PoolEnv);
//创建IOCP线程池
g_pThreadpoolIO = CreateThreadpoolIo(hTxtFile,(PTP_WIN32_IO_CALLBACK)IoCompletionCallback,
hTxtFile,&PoolEnv);
//启动IOCP线程池
StartThreadpoolIo(g_pThreadpoolIO);
//写入UNICODE文件的前缀码,以便正确打开
ST_MY_OVERLAPPED* pMyOl = (ST_MY_OVERLAPPED*)GRS_CALLOC(sizeof(ST_MY_OVERLAPPED));
GRS_ASSERT(NULL != pMyOl);
pMyOl->m_dwOp = GRS_OP_WRITE;
pMyOl->m_hFile = hTxtFile;
pMyOl->m_pData = GRS_CALLOC(sizeof(WORD));
GRS_ASSERT(NULL != pMyOl->m_pData);
*((WORD*)pMyOl->m_pData) = MAKEWORD(0xff,0xfe);//UNICODE文本文件需要的前缀
pMyOl->m_nLen = sizeof(WORD);
//偏移文件指针
pMyOl->m_ol.Offset = g_liFilePointer.LowPart;
pMyOl->m_ol.OffsetHigh = g_liFilePointer.HighPart;
g_liFilePointer.QuadPart += pMyOl->m_nLen;
pMyOl->m_dwTimestamp = GetTickCount();//记录时间戳
WriteFile((HANDLE)hTxtFile,pMyOl->m_pData,pMyOl->m_nLen,&pMyOl->m_dwWrite,(LPOVERLAPPED)&pMyOl->m_ol);
//等待IOCP线程池完成操作
WaitForThreadpoolIoCallbacks(g_pThreadpoolIO,FALSE);
//启动写入线程进行日志写入操作
for(int i = 0; i < GRS_MAXWTHREAD; i ++)
{
ahWThread[i] = GRS_BEGINTHREAD(WThread,hTxtFile);
}
//让主线等待这些写入线程结束
WaitForMultipleObjects(GRS_MAXWTHREAD,ahWThread,TRUE,INFINITE);
for(int i = 0; i < GRS_MAXWTHREAD; i ++)
{
CloseHandle(ahWThread[i]);
}
//关闭IOCP线程池
CloseThreadpoolIo(g_pThreadpoolIO);
//关闭日志文件
if(INVALID_HANDLE_VALUE != hTxtFile )
{
CloseHandle(hTxtFile);
hTxtFile = INVALID_HANDLE_VALUE;
}
_tsystem(_T("PAUSE"));
return 0;
}
VOID CALLBACK IoCompletionCallback(PTP_CALLBACK_INSTANCE Instance,PVOID Context,PVOID Overlapped
,ULONG IoResult,ULONG_PTR NumberOfBytesTransferred,PTP_IO Io)
{
GRS_USEPRINTF();
if(NO_ERROR != IoResult)
{
GRS_PRINTF(_T("I/O操作出错,错误码:%u\n"),IoResult);
return;
}
ST_MY_OVERLAPPED* pMyOl = CONTAINING_RECORD((LPOVERLAPPED)Overlapped,ST_MY_OVERLAPPED,m_ol);
DWORD dwCurTimestamp = GetTickCount();
switch(pMyOl->m_dwOp)
{
case GRS_OP_WRITE:
{
GRS_PRINTF(_T("线程[0x%x]得到IO完成通知,完成操作(%s),缓冲(0x%08x)长度(%ubytes),写入时间戳(%u)当前时间戳(%u)时差(%u)\n"),
GetCurrentThreadId(),GRS_OP_WRITE == pMyOl->m_dwOp?_T("Write"):_T("Read"),
pMyOl->m_pData,pMyOl->m_nLen,pMyOl->m_dwTimestamp,dwCurTimestamp,dwCurTimestamp - pMyOl->m_dwTimestamp);
GRS_SAFEFREE(pMyOl->m_pData);
GRS_SAFEFREE(pMyOl);
}
break;
case GRS_OP_READ:
{
}
break;
case GRS_OP_EXIT:
{
GRS_PRINTF(_T("IOCP线程[0x%x]得到退出通知,时间戳(%u)当前时间戳(%u)时差(%u),IOCP线程退出\n"),
GetCurrentThreadId(),pMyOl->m_dwTimestamp,dwCurTimestamp,dwCurTimestamp - pMyOl->m_dwTimestamp);
GRS_SAFEFREE(pMyOl->m_pData);
GRS_SAFEFREE(pMyOl);
}
break;
default:
{
}
break;
}
//GRS_SAFEFREE(pMyOl->m_pData);
//GRS_SAFEFREE(pMyOl);
}
#define MAX_LOGLEN 256
DWORD WINAPI WThread(LPVOID lpParameter)
{
GRS_USEPRINTF();
TCHAR pTxtContext[MAX_LOGLEN] = {};
ST_MY_OVERLAPPED* pMyOl = NULL;
size_t szLen = 0;
LPTSTR pWriteText = NULL;
StringCchPrintf(pTxtContext,MAX_LOGLEN,_T("这是一条模拟的日志记录,由线程[0x%x]写入\r\n")
,GetCurrentThreadId());
StringCchLength(pTxtContext,MAX_LOGLEN,&szLen);
szLen += 1;
int i = 0;
for(;i < GRS_MAXWRITEPERTHREAD; i ++)
{
pWriteText = (LPTSTR)GRS_CALLOC(szLen * sizeof(TCHAR));
GRS_ASSERT(NULL != pWriteText);
StringCchCopy(pWriteText,szLen,pTxtContext);
//为每个操作申请一个自定义OL结构体,实际应用中这里考虑使用内存池
pMyOl = (ST_MY_OVERLAPPED*)GRS_CALLOC(sizeof(ST_MY_OVERLAPPED));
GRS_ASSERT(NULL != pMyOl);
pMyOl->m_dwOp = GRS_OP_WRITE;
pMyOl->m_hFile = (HANDLE)lpParameter;
pMyOl->m_pData = pWriteText;
pMyOl->m_nLen = szLen * sizeof(TCHAR);
//这里使用原子操作同步文件指针,写入不会相互覆盖
//这个地方体现了lock-free算法的精髓,使用了基本的CAS操作控制文件指针
//比传统的使用关键代码段并等待的方法,这里用的方法要轻巧的多,付出的代价也小
*((LONGLONG*)&pMyOl->m_ol.Pointer) = InterlockedCompareExchange64(&g_liFilePointer.QuadPart,
g_liFilePointer.QuadPart + pMyOl->m_nLen,g_liFilePointer.QuadPart);
pMyOl->m_dwTimestamp = GetTickCount();//记录时间戳
//写入
WriteFile((HANDLE)lpParameter,pMyOl->m_pData,pMyOl->m_nLen,&pMyOl->m_dwWrite,(LPOVERLAPPED)&pMyOl->m_ol);
if( ERROR_IO_PENDING != GetLastError() )
{
CancelThreadpoolIo(g_pThreadpoolIO);
}
}
return i;
}
===========================================华丽丽的分割线===================================================
上面的代码看上去中规中矩,逻辑也很清晰,无非就是启动了很多线程朝一个模拟的日志文件中写入一条日志,因为是重叠IO方式在操作文件,所以要自己控制文件指针位置,代码中用了InterlockedCompareExchange64这个方法来实现了一个简单的lock-free方式的同步控制,从根本上避免了使用传统的利用关键代码段或互斥对象等基于互锁方法可能引起的死锁问题,同时性能上也有一定提升,实在是居家旅行开发软件不可或缺的良药.
看到这里先别跟我一样得意,因为上面的代码运行后就会产生一个访问非法内存错误,并且每次执行访问的地址都不一样.为此我折腾了3个多小时(期间抽烟3根,电话1个跟哥们要FQ软件找资料,因为国内都是API的说明,没有调用,更不会有可执行的源码,自我这篇文章起,算是填补了国内此API调用详解的空白),不要以为是简单的野指针问题或者是指针未初始化问题,因为出错的位置是在系统dll中,没有我的代码的上下文,所以调试难度是灰常之高(题外音:有多高啊?有3层楼那么高啦!).
再试图搜索E文资料也无果的情况下,我还是折回头仔细的阅读MSDN中的文档,在StartThreadpoolIo的Remark段,我看到了下面的说明:
You must call this function before initiating each asynchronous I/O operation on the file handle bound to the I/O completion object. Failure to do so will cause the thread pool to ignore an I/O operation when it completes and will cause memory corruption.
看过之后我乐了,ok,先翻译给大家,以方便那些E文不好的兄弟们也能看懂,翻译的不好不要拍砖哈,它说的大意是:你必须每次在调用异步IO操作前调用这个函数(StartThreadpoolIo),否则线程池就会引起内存损坏(corruption原意"腐烂").
原来如彼,看来其实是自个瞎折腾了3个多小时,其实早早的老老实实看MSDN文档,就不会有这个鬼问题了.唉,惭愧,一再提醒大家有问题就看MSDN,而不是上网,结果自个却忘记了.
据此修改前面的代码如下:
===========================================华丽丽的分割线===================================================
#define _WIN32_WINNT 0x0600
#include
#include
#include
#define GRS_ALLOC(sz) HeapAlloc(GetProcessHeap(),0,sz)
#define GRS_CALLOC(sz) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz)
#define GRS_SAFEFREE(p) if(NULL != p){HeapFree(GetProcessHeap(),0,p);p=NULL;}
#define GRS_USEPRINTF() TCHAR pBuf[1024] = {}
#define GRS_PRINTF(...) \
StringCchPrintf(pBuf,1024,__VA_ARGS__);\
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),pBuf,lstrlen(pBuf),NULL,NULL);
#define GRS_ASSERT(s) if(!(s)) {::DebugBreak();}
VOID GetAppPath(LPTSTR pszBuffer)
{
DWORD dwLen = 0;
if(0 == (dwLen = ::GetModuleFileName(NULL,pszBuffer,MAX_PATH)))
{
return;
}
DWORD i = dwLen;
for(; i > 0; i -- )
{
if( '\\' == pszBuffer[i] )
{
pszBuffer[i + 1] = '\0';
break;
}
}
}
#define GRS_BEGINTHREAD(Fun,Param) CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Fun,Param,0,NULL)
#define GRS_MAXWRITEPERTHREAD 100 //每个线程最大写入次数
#define GRS_MAXWTHREAD 20 //写入线程数量
#define GRS_OP_READ 0x1 //读取操作
#define GRS_OP_WRITE 0x2 //写入操作
#define GRS_OP_EXIT 0x3 //关闭IOCP操作
struct ST_MY_OVERLAPPED
{
OVERLAPPED m_ol; //Overlapped 结构,不一定非得是第一个成员
HANDLE m_hFile; //操作的文件句柄
DWORD m_dwOp; //操作类型GRS_OP_READ/GRS_OP_WRITE
LPVOID m_pData; //操作的数据
UINT m_nLen; //操作的数据长度
DWORD m_dwWrite; //写入字节数
DWORD m_dwTimestamp; //起始操作的时间戳
};
//IOCP线程池回调函数,实际就是完成通知响应函数
VOID CALLBACK IoCompletionCallback(PTP_CALLBACK_INSTANCE Instance,PVOID Context,PVOID Overlapped
,ULONG IoResult,ULONG_PTR NumberOfBytesTransferred,PTP_IO Io);
//写文件的线程
DWORD WINAPI WThread(LPVOID lpParameter);
//当前操作的文件对象的指针
LARGE_INTEGER g_liFilePointer = {};
//IOCP线程池
PTP_IO g_pThreadpoolIO = NULL;
int _tmain()
{
GRS_USEPRINTF();
TCHAR pFileName[MAX_PATH] = {};
GetAppPath(pFileName);
StringCchCat(pFileName,MAX_PATH,_T("IOCPFile.txt"));
HANDLE ahWThread[GRS_MAXWTHREAD] = {};
DWORD dwWrited = 0;
//创建文件
HANDLE hTxtFile = CreateFile(pFileName
,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
if(INVALID_HANDLE_VALUE == hTxtFile)
{
GRS_PRINTF(_T("CreateFile(%s)失败,错误码:0x%08x\n")
,pFileName,GetLastError());
_tsystem(_T("PAUSE"));
return 0;
}
//初始化线程池环境数据结构
TP_CALLBACK_ENVIRON PoolEnv = {};
InitializeThreadpoolEnvironment(&PoolEnv);
//创建IOCP线程池
g_pThreadpoolIO = CreateThreadpoolIo(hTxtFile,(PTP_WIN32_IO_CALLBACK)IoCompletionCallback,
hTxtFile,&PoolEnv);
//启动IOCP线程池
StartThreadpoolIo(g_pThreadpoolIO);
//写入UNICODE文件的前缀码,以便正确打开
ST_MY_OVERLAPPED* pMyOl = (ST_MY_OVERLAPPED*)GRS_CALLOC(sizeof(ST_MY_OVERLAPPED));
GRS_ASSERT(NULL != pMyOl);
pMyOl->m_dwOp = GRS_OP_WRITE;
pMyOl->m_hFile = hTxtFile;
pMyOl->m_pData = GRS_CALLOC(sizeof(WORD));
GRS_ASSERT(NULL != pMyOl->m_pData);
*((WORD*)pMyOl->m_pData) = MAKEWORD(0xff,0xfe);//UNICODE文本文件需要的前缀
pMyOl->m_nLen = sizeof(WORD);
//偏移文件指针
pMyOl->m_ol.Offset = g_liFilePointer.LowPart;
pMyOl->m_ol.OffsetHigh = g_liFilePointer.HighPart;
g_liFilePointer.QuadPart += pMyOl->m_nLen;
pMyOl->m_dwTimestamp = GetTickCount();//记录时间戳
WriteFile((HANDLE)hTxtFile,pMyOl->m_pData,pMyOl->m_nLen,&pMyOl->m_dwWrite,(LPOVERLAPPED)&pMyOl->m_ol);
//等待IOCP线程池完成操作
WaitForThreadpoolIoCallbacks(g_pThreadpoolIO,FALSE);
//启动写入线程进行日志写入操作
for(int i = 0; i < GRS_MAXWTHREAD; i ++)
{
ahWThread[i] = GRS_BEGINTHREAD(WThread,hTxtFile);
}
//让主线等待这些写入线程结束
WaitForMultipleObjects(GRS_MAXWTHREAD,ahWThread,TRUE,INFINITE);
for(int i = 0; i < GRS_MAXWTHREAD; i ++)
{
CloseHandle(ahWThread[i]);
}
//关闭IOCP线程池
CloseThreadpoolIo(g_pThreadpoolIO);
//关闭日志文件
if(INVALID_HANDLE_VALUE != hTxtFile )
{
CloseHandle(hTxtFile);
hTxtFile = INVALID_HANDLE_VALUE;
}
_tsystem(_T("PAUSE"));
return 0;
}
VOID CALLBACK IoCompletionCallback(PTP_CALLBACK_INSTANCE Instance,PVOID Context,PVOID Overlapped
,ULONG IoResult,ULONG_PTR NumberOfBytesTransferred,PTP_IO Io)
{
GRS_USEPRINTF();
if(NO_ERROR != IoResult)
{
GRS_PRINTF(_T("I/O操作出错,错误码:%u\n"),IoResult);
return;
}
ST_MY_OVERLAPPED* pMyOl = CONTAINING_RECORD((LPOVERLAPPED)Overlapped,ST_MY_OVERLAPPED,m_ol);
DWORD dwCurTimestamp = GetTickCount();
switch(pMyOl->m_dwOp)
{
case GRS_OP_WRITE:
{
GRS_PRINTF(_T("线程[0x%x]得到IO完成通知,完成操作(%s),缓冲(0x%08x)长度(%ubytes),写入时间戳(%u)当前时间戳(%u)时差(%u)\n"),
GetCurrentThreadId(),GRS_OP_WRITE == pMyOl->m_dwOp?_T("Write"):_T("Read"),
pMyOl->m_pData,pMyOl->m_nLen,pMyOl->m_dwTimestamp,dwCurTimestamp,dwCurTimestamp - pMyOl->m_dwTimestamp);
GRS_SAFEFREE(pMyOl->m_pData);
GRS_SAFEFREE(pMyOl);
}
break;
case GRS_OP_READ:
{
}
break;
case GRS_OP_EXIT:
{
GRS_PRINTF(_T("IOCP线程[0x%x]得到退出通知,时间戳(%u)当前时间戳(%u)时差(%u),IOCP线程退出\n"),
GetCurrentThreadId(),pMyOl->m_dwTimestamp,dwCurTimestamp,dwCurTimestamp - pMyOl->m_dwTimestamp);
GRS_SAFEFREE(pMyOl->m_pData);
GRS_SAFEFREE(pMyOl);
}
break;
default:
{
}
break;
}
//GRS_SAFEFREE(pMyOl->m_pData);
//GRS_SAFEFREE(pMyOl);
}
#define MAX_LOGLEN 256
DWORD WINAPI WThread(LPVOID lpParameter)
{
GRS_USEPRINTF();
TCHAR pTxtContext[MAX_LOGLEN] = {};
ST_MY_OVERLAPPED* pMyOl = NULL;
size_t szLen = 0;
LPTSTR pWriteText = NULL;
StringCchPrintf(pTxtContext,MAX_LOGLEN,_T("这是一条模拟的日志记录,由线程[0x%x]写入\r\n")
,GetCurrentThreadId());
StringCchLength(pTxtContext,MAX_LOGLEN,&szLen);
szLen += 1;
int i = 0;
for(;i < GRS_MAXWRITEPERTHREAD; i ++)
{
pWriteText = (LPTSTR)GRS_CALLOC(szLen * sizeof(TCHAR));
GRS_ASSERT(NULL != pWriteText);
StringCchCopy(pWriteText,szLen,pTxtContext);
//为每个操作申请一个自定义OL结构体,实际应用中这里考虑使用内存池
pMyOl = (ST_MY_OVERLAPPED*)GRS_CALLOC(sizeof(ST_MY_OVERLAPPED));
GRS_ASSERT(NULL != pMyOl);
pMyOl->m_dwOp = GRS_OP_WRITE;
pMyOl->m_hFile = (HANDLE)lpParameter;
pMyOl->m_pData = pWriteText;
pMyOl->m_nLen = szLen * sizeof(TCHAR);
//这里使用原子操作同步文件指针,写入不会相互覆盖
//这个地方体现了lock-free算法的精髓,使用了基本的CAS操作控制文件指针
//比传统的使用关键代码段并等待的方法,这里用的方法要轻巧的多,付出的代价也小
*((LONGLONG*)&pMyOl->m_ol.Pointer) = InterlockedCompareExchange64(&g_liFilePointer.QuadPart,
g_liFilePointer.QuadPart + pMyOl->m_nLen,g_liFilePointer.QuadPart);
pMyOl->m_dwTimestamp = GetTickCount();//记录时间戳
//注意添加下面这句,否则后果不堪设想
StartThreadpoolIo(g_pThreadpoolIO);
//写入
WriteFile((HANDLE)lpParameter,pMyOl->m_pData,pMyOl->m_nLen,&pMyOl->m_dwWrite,(LPOVERLAPPED)&pMyOl->m_ol);
if( ERROR_IO_PENDING != GetLastError() )
{
CancelThreadpoolIo(g_pThreadpoolIO);
}
}
return i;
}
===========================================华丽丽的分割线===================================================
至此,这个模拟写日志的示例代码终于可以正常工作了,我的在线视频教程第二部分中的线程池部分,也成功加入这个例子,到时候一定要在讲解中详细讲解这个例子以及新的Windows2008线程池的用法(该线程池还可以用于Vista和Win7上,本文例子就是在Win7上用VS2008调试通过).
如果你看懂了这个例子,那么我们最后来综合看下新的线程池函数究竟带来了什么?
首先,我觉得带来的就是麻烦(因为麻烦了我几乎一个上午,呵呵),调用麻烦,每次都要StartThreadpoolIo,还有就是每个句柄都要CreateThreadpoolIo创建一个该死的不知道是何物的指针,并且真实的设计中还要句柄和这个指针进行hash关联或map,真麻烦;(还是觉得老的BindIoCompletionCallback 好用,一个函数包打天下)
其次,既然是新的线程池,那么就可以调用新的API,主要是以下这些:
DisassociateCurrentThreadFromCallback
FreeLibraryWhenCallbackReturns
LeaveCriticalSectionWhenCallbackReturns
ReleaseMutexWhenCallbackReturns
ReleaseSemaphoreWhenCallbackReturns
SetEventWhenCallbackReturns
从而可以方便的指定在执行完完成通知(即调用完回调函数后)可以释放一些资源(具体看函数名就可以明白);当然这些函数要在回调函数内开始时就调用.
再次,性能有所提升?(注意是个问号),性能问题我没有再进一步去深究,究竟较传统的线程池函数是否有所提升,我不知道,但是有一点是可以肯定的,就是在线程数量上,新的线程池是一点也不保守的,在我的双核系统上,调试暂停的情况下可以看到系统至少创建了10个以上的线程来调用那个回调函数(各位可以调试下,场面肯定很壮观,看看几十个线程的效果).这个让我感觉很汗,是不是微软已经觉得未来等大家都明白新线程怎么用的时候,系统都已经是至少十几核以上的CPU了?太前卫了吧?由此在CPU数(或内核数)有限的系统上,这么多线程调度会不会低效?这些问题我没有答案.
最后,这一次的线程池改进,程序能够控制的线程池特性依然有限,这个IOCP池依然无法控制并发线程数量,这个算不算失败?(当然普通的那个Work线程池可以控制并发线程数量了)
其实对于线程池,我比较关心的就是性能(当然我想这个微软应该有考虑),安全性和并发数量控制.关于安全性,很遗憾,这次的新线程池API中依然没有看到,比如让线程池中的线程以某一个帐号权限运行,这个操作依然没有,对于安全的服务器编程来说,我觉得这个很重要.不然都是系统管理员权限运行或system帐号运行,网管可就有的忙了.
ok,今天就说到这吧,时间仓促,没有细说,也许也没有说清楚,请大家见谅,侯老师说:在源代码面前没有秘密.所以我将完整的代码都贡献出来,供大家解剖.关于这几个API大家可以再搜搜资料,看看MSDN,IOCP不懂可以看看我博客的其它文章,或是搜搜别人的文章.如果对服务器编程很感兴趣,就可以去北风网看本人的在线视频教程.