Hi, all
最终采用了下面这篇文章种提到的做法
>
http://libo.deng.blog.163.com/blog/static/40157422200772132422119/
>编写大容量和健壮的服务器系列—处理IOCP资源释放
方式二
使用一个Hash表,对每个连接定义一个连接上下文,按socket值索引,把一些资源,例如信令缓存指针置于hash中的上下文。
HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort,
ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads);
一个关键的地方是在把 client socket 关联到 完成端口,调用 CreateIoCompletionPort 的时候,
CompletionKey 不要按常见的做法,把 PER_HANDLE_DATA 的指针传进行去,而是只把 client socket 按值传进去。
然后把 CompletionKey 和 PER_HANDLE_DATA 保存到一个 Hash 表中。
CreateIoCompletionPort:
ULONG_PTR completionKey = client;
PER_HANDLE_DATA * perHandleData = GlobalAlloc(xxx);
sessionManager->put( completionKey, perHandleData );
CreateIoCompletionPort( client, completionPort, completionKey, 0 )
GetQueuedCompletionStatus:
ULONG_PTR completionKey = 0;
GetQueuedCompletionStatus( completionPort, &bytesTransferred, &completionKey, ... );
PER_HANDLE_DATA * perHandleData = sessionManager->get( completionKey );
if( NULL != perHandleData ) {
}
closesocket:
sessionManager->remove( completionKey );
closesocket( perHandleData->socket );
GlobalFree( perHandleData );
>一个可能的执行顺序如下:
>1.线程 A 执行了 closesocket
>2.线程 B 取得了一个被取消的 异步 IO 操作
>3.线程 B 通过访问 PER_HANDLE_DATA 知道这是一个通过 closesocket 触发的 取消操作,因此简单地忽略
>4.线程 A 释放 PER_HANDLE_DATA
重新回到这四个步骤,在 步骤1 中,在 closesocket 之前,先根据 completionKey 从 sessionManager 中删除掉 perHandleData 。
之后,在 线程B 中得到了 completionKey ,但是根据 completionKey 无法从 sessionManager 中找到 perHandlerData 。
这样,无论上面的四个步骤按什么顺序执行,都不会导致访问已经被释放内存。
Best regards,
stephen.nil
2008-05-11