我仍然对NumberOfConcurrentThreads
中的CreateIoCompletionPort()
参数感到困惑。我已阅读并重新阅读MSDN dox,但引用
此值限制与之关联的可运行线程的数量 完成港口。
仍然困扰着我。
问题
我们假设我将此值指定为4.在这种情况下,这是否意味着:
1)一个线程可以调用GetQueuedCompletionStatus()
(此时我可以允许另外3个线程进行此调用),然后一旦该调用返回(即我们有一个完成包),我就可以拥有4个线程再次调用此函数,
或
2)一个线程可以调用GetQueuedCompletionStatus()
(此时我可以允许另外3个线程进行此调用),然后一旦该调用返回(即我们有一个完成包),我继续处理该数据包。只有当我完成处理数据包后,我才会调用GetQueuedCompletionStatus()
,此时我可以然后让4个线程再次调用此函数。
看到我的困惑?使用短语' runnable threads'。
我认为可能是后者,因为上面的链接也引用了
如果您的交易需要长时间的计算,则更大 并发值将允许更多线程运行。每次完成 数据包可能需要更长的时间才能完成,但会有更多的完成数据包 同时处理。
这最终会影响我们设计服务器的方式。考虑从客户端接收数据的服务器,然后将该数据回显给日志记录服务器。这是我们的线程例程的样子:
DWORD WINAPI ServerWorkerThread(HANDLE hCompletionPort)
{
DWORD BytesTransferred;
CPerHandleData* PerHandleData = nullptr;
CPerOperationData* PerIoData = nullptr;
while (TRUE)
{
if (GetQueuedCompletionStatus(hCompletionPort, &BytesTransferred,
(PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
// OK, we have 'BytesTransferred' of data in 'PerIoData', process it:
// send the data onto our logging servers, then loop back around
send(...);
}
}
return 0;
}
现在假设我有一台四核机器;如果我在NumberOfConcurrentThreads
的调用中将CreateIoCompletionPort()
保留为零,我将有四个线程运行ServerWorkerThread()
。细
我担心的是,由于网络流量,send()
电话可能需要很长时间。因此,我可能会从无法出列的客户端接收大量数据,因为所有四个线程都需要很长时间才能发送数据?!
我是否错过了这一点?
更新07.03.2018 (现已解决:见this comment。)
我的机器上运行了8个线程,每个线程运行ServerWorkerThread()
:
DWORD WINAPI ServerWorkerThread(HANDLE hCompletionPort)
{
DWORD BytesTransferred;
CPerHandleData* PerHandleData = nullptr;
CPerOperationData* PerIoData = nullptr;
while (TRUE)
{
if (GetQueuedCompletionStatus(hCompletionPort, &BytesTransferred,
(PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
switch (PerIoData->Operation)
{
case CPerOperationData::ACCEPT_COMPLETED:
{
// This case is fired when a new connection is made
while (1) {}
}
}
}
我只有一个未完成的AcceptEx()
电话;当它被新连接填满时我会发布另一个连接。我不等待AcceptEx()
收到数据。
我按如下方式创建完成端口:
CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 4)
现在,因为我只在完成端口允许4个线程,我认为因为我保持线程忙(即它们没有进入等待状态),当我尝试做第五个连接,完成包不会出列因此会挂起!然而,这种情况并非如此;我可以与我的服务器建立5个甚至6个连接!这表明即使我的最大允许线程数(4)已经运行,我仍然可以将数据包出列? 这就是为什么我感到困惑!
答案 0 :(得分:1)
完成端口 - 实际上是KQUEUE
对象。 NumberOfConcurrentThreads 对应MaximumCount
来自I / O完成端口队列可以满足的最大并发线程数等待。
当与之关联的可运行线程的总数时 完成端口达到并发值,系统阻塞 执行与该完成相关联的任何后续线程 port,直到可运行线程数低于并发 值。
它很糟糕,并没有完全说出来。当线程调用KeRemoveQueue
(GetQueuedCompletionStatus
内部调用它)系统将数据包返回到线程only if Queue->CurrentCount < Queue->MaximumCount
,即使队列中存在数据包。系统不阻止任何线程当然。从另一边寻找KiInsertQueue
- 即使某些线程在数据包上等待 - 它仅在Queue->CurrentCount < Queue->MaximumCount
的情况下被激活。
还会查看Queue->CurrentCount
的更改方式和时间。查找KiActivateWaiterQueue
(当前线程即将进入等待状态时调用此函数)和KiUnlinkThread
。通常 - 当线程开始等待任何对象(或另一个队列)系统调用KiActivateWaiterQueue
时 - 它会递减CurrentCount
并且可能(如果队列中存在数据包并且变为Queue->CurrentCount < Queue->MaximumCount
并且线程等待数据包)返回数据包等待线程。另一方面,当线程停止等待时 - KiUnlinkThread
被调用。它增加CurrentCount
。
你的两种变体都是错的。任何线程数都可以调用GetQueuedCompletionStatus()
。系统当然不会阻止任何后续线程的执行。例如 - 你有MaximumCount = 4
的队列。你可以将10个数据包排队到队列。从并发的7个线程中调用GetQueuedCompletionStatus()
。但只有4个来自它的包。另一个将等待(尽管队列中还有6个数据包)。如果从队列中删除数据包的某些线程开始等待 - 系统只是等待并将数据包返回到另一个线程等待队列。或者如果线程(之前已经从此队列中移除数据包(Thread->Queue == Queue
) - 所以活动线程)再次调用KeRemoveQueue
将是Queue->CurrentCount -= 1;