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

从MSDN注意CreateIoCompletionPort函数中的CompletionKey:

使用CompletionKey参数来帮助您的应用程序跟踪哪些I / O操作已经完成。 这个值不被CreateIoCompletionPort用于function控制; 而是将其附加到与I / O完成端口关联时在FileHandle参数中指定的文件句柄。 该完成密钥对于每个文件句柄应该是唯一的,并且在整个内部完成排队过程中伴随文件句柄。 当完成数据包到达时,它在GetQueuedCompletionStatus函数调用中返回。 CompletionKey参数也被PostQueuedCompletionStatus函数用来排队你自己的特殊用途完成数据包。

以上的评论给我留下了一个问题。 为什么使用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; 

并在完成后通过CONTAINING_RECORDmacros进行检索?

很酷,我只相信CompletionKey是每个句柄的上下文,而扩展的重叠结构是每个I / O。 但是这样的devise背后的哲学是什么?在什么情况下,使用CompletionKey而不是用户上下文中的扩展重叠结构是必要的?