PostQueuedCompletionStatus函数与GetQueuedCompletionStatus函数

本文介绍了PostQueuedCompletionStatus和GetQueuedCompletionStatus在多线程IO完成端口中的使用。PostQueuedCompletionStatus用于向工作者线程发送完成数据包,让它们根据特定参数决定退出。GetQueuedCompletionStatus则用于从完成端口获取已完成的I/O操作信息。文章还讨论了OVERLAPPED结构体的扩展以及如何利用CONTAINING_RECORD宏获取结构体首地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PostQueuedCompletionStatus函数,向每个工作者线程都发送—个特殊的完成数据包。该函数会指示每个线程都“立即结束并退出”.下面是PostQueuedCompletionStatus函数的定义:
BOOL PostQueuedCompletionStatus(
    HANDLE CompletlonPort,
    DW0RD dwNumberOfBytesTrlansferred,
    DWORD dwCompletlonKey,
LPOVERLAPPED lpoverlapped,

);

       其中,CompletionPort参数指定想向其发送一个完成数据包的完成端口对象。而就dwNumberOfBytesTransferred,dwCompletionKey和lpOverlapped这三个参数来说.每—个都允许我们指定—个值,直接传递给GetQueuedCompletionStatus函数中对应的参数。这样—来。—个工作者线程收到传递过来的三个GetQueuedCompletionStatus函数参数后,便可根据由这三个参数的某一个设置的特殊值,决定何时应该退出。例如,可用dwCompletionPort参数传递0值,而—个工作者线程会将其解释成中止指令。一旦所有工作者线程都已关闭,便可使用CloseHandle函数,关闭完成端口。最终安全退出程序。
        PostQueuedCompletionStatus函数提供了一种方式来与线程池中的所有线程进行通信。如,当用户终止服务应用程序时,我们想要所有线程都完全利索地退出。但是如果各线程还在等待完成端口而又没有已完成的I/O 请求,那么它们将无法被唤醒。
       通过为线程池中的每个线程都调用一次PostQueuedCompletionStatus,我们可以将它们都唤醒。每个线程会对GetQueuedCompletionStatus的返回值进行检查,如果发现应用程序正在终止,那么它们就可以进行清理工作并正常地退出。

GetQueuedCompletionStatus函数有个OVERLAPPED结构,很多资料上都采用不同的结构体来扩展该结构,比如有的资料定义:
typedef struct _OVERLAPPEDPLUS
{
OVERLAPPED ol;
SOCKET s, sclient;
int OpCode;
WSABUF wbuf;
DWORD dwBytes, dwFlags;
}OVERLAPPEDPLUS;

然后,当GetQueuedCompletionStatus(hIocp, &dwBytesXfered,(PULONG_PTR)&PerHandleKey, &Overlap, INFINITE);函数返回时候,人们常用OverlapPlus = CONTAINING_RECORD(Overlap, OVERLAPPEDPLUS, ol)得到一些信息。比如此时端口上完成的是什么操作,数据是什么等,还有,系统如何做到自动填充上述的结构的,也就是说,系统怎么知道在Overlap->OpCode存放的应该是操作类型,如读,写操作,而在Overlap->wbuf存放的应该是读写数据。


Overlap->OpCode,操作类型是在投递WSASend,WSARecv的时候,由你自己指定填充这个字段。

因为是非堵塞的,等于投递到与套接字相关联的完成端口上,系统会把把WSASend对应的缓冲区提交到底层缓冲,也可以把WSARecv投递的缓冲区,用接收到的数据填充,每一个WSASend,WSARecv,都应有新申请一个overlaspped plus结构提交,以存放本次投递的IO操作的相关数据,——单IO操作数据所以工作器线程中,从完成端口队列中get得到一个完成包的时候,可以根据单句柄数据知道在这个完成端口上是哪一个套接字投递的IO操作完成了,从get到的overlapped中得到相关的已经完成IO数据和信息,并作相应的处理。比如投递了1M,完成包却告知只完成512K,那么你就知道要把余下的512K继续投递WSASend,当然上一个WSASend的Overlapped这个时候可以重用到下一个WSASend中,这个是允许的,可以用一个字段存放全部1M,把余下未Send成功512k放到wbuf中,继续投递或者投递WSARecv1M数据,却收到一个512K的完成通知,那么你要继续投递WSARecv,当然前一个WSARecv的overlapped也可以重用,不过需要一些处理,把已经接收到的512K保存到某个字段中,再投递一个512K的请求去接收完成端口内部,对投递的Overlapped的填充,好像只有WSARecv的时候填充WSABUF,其他都是投递IO前,代码中显式填充,并投递的。至于完成了多少个字节,是在lpNumberOfBytes中得到。

对GetQueuedCompletionStatus函数解释:
实现从指定的IOCP获取CP。当CP队列为空时,对此函数的调用将被阻塞,而不是一直等待I/O的完成。当CP队列不为空时,被阻塞的线程将以后进先出(LIFO)顺序被释放。对于IOCP机制,它允许多线程并发调用GetQueuedCompletionStatus函数,最大并发数是在调用CreateIoCompletionPort函数时指定的,超出最大并发数的调用线程,将被阻塞。函数解释如下:  
  声明:  
  BOOL   GetQueuedCompletionStatus(  
          HANDLE   CompletionPort,    
          LPDWORD   lpNumberOfBytes,    
          PULONG_PTR   lpCompletionKey,    
          LPOVERLAPPED   *lpOverlapped,    
          DWORD   dwMilliseconds);  
  调用参数:  
  CompletionPort:指定的IOCP,该值由CreateIoCompletionPort函数创建。  
  lpnumberofbytes:一次完成后的I/O操作所传送数据的字节数。  
  lpcompletionkey:当文件I/O操作完成后,用于存放与之关联的CK。  
  lpoverlapped:为调用IOCP机制所引用的OVERLAPPED结构。  
  dwmilliseconds:用于指定调用者等待CP的时间。  
  返回值:  
  调用成功,则返回非零数值,相关数据存于lpNumberOfBytes、lpCompletionKey、lpoverlapped变量中。失败则返回零值。

 

#define CONTAINING_RECORD(address, type, field) ((type *)( /
     (PCHAR)(address) - /
     (ULONG_PTR)(&((type *)0)->field)))

用于取得内存中任何结构体的首地址,要提供的参数是:结构体中某个成员(field)的地址address、结构体的类型type、提供地址那个成员的名字field。(ULONG_PTR)(&((type *)0)->field)),它表示结构体type的field字段相对type首地址的内存地址偏移量,也可以如此理解:结构体首地址从0开始数,field的偏移就是(ULONG_PTR)(&((type *)0)->field))type结构体的field字段的地址减去field字段在结构体中的偏移,就是结构体的首地址。

如:
typedef struct tagSP_IocpEvent {
 enum { SP_IOCP_MAX_IOV = 8 };
 enum { eEventUnknown, eEventRecv, eEventSend, eEventTimer };
 OVERLAPPED mOverlapped;
 int mType;
 WSABUF mWsaBuf;
 void ( * mOnTimer ) ( void * );
 int mHeapIndex;
 struct timeval mTimeout;
} SP_IocpEvent_t;
SP_IocpEvent_t * iocpEvent = CONTAINING_RECORD( overlapped, SP_IocpEvent_t, mOverlapped );

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值