为什么I/O完成端口中的CompletionKey?

Y.Z*_*Y.Z 5 windows multithreading winsock overlapped-io io-completion-ports

来自MSDN的关于CreateIoCompletionPort函数中的CompletionKey的评论:

使用CompletionKey参数可帮助您的应用程序跟踪已完成的I/O操作.CreateIoCompletionPort不使用此值进行功能控制; 相反,它在与I/O完成端口关联时附加到FileHandle参数中指定的文件句柄.对于每个文件句柄,此完成键应该是唯一的,并且它在整个内部完成排队过程中伴随文件句柄.当完成数据包到达时,它将在GetQueuedCompletionStatus函数调用中返回.PostQueuedCompletionStatus函数还使用CompletionKey参数来排队您自己的专用完成数据包.

上述言论给我一个问题.为什么使用CompletionKey,因为我们可以将用户上下文与扩展重叠结构中的文件句柄相关联,如下所示:

typedef struct s_overlappedplus
{
    OVERLAPPED ol;
    int op_code;
    /*we can alternatively put user context over here instead of CompletionKey*/
    LPVOID user_context;
} t_overlappedplus;
Run Code Online (Sandbox Code Playgroud)

完成后通过CONTAINING_RECORD宏进行检索?

很酷,我只相信CompletionKey是每个句柄的上下文,而扩展的重叠结构是每I/O一个.但是这种设计背后的哲学是什么,在什么情况下可以使用CompletionKey而不是在用户上下文中使用扩展的重叠结构?