如何在使用 asyncio ProactorEventLoop 时分配线程池



我目前正在Python 3.7中使用asyncio,并使用asyncio.start_server((函数编写TCP服务器 请参考此示例:https://docs.python.org/3/library/asyncio-stream.html

也试试异步。使用"I/O 完成端口"(IOCP( 的 ProactorEventLoop

根据这个Microsoft官方文档 https://learn.microsoft.com/en-ca/windows/win32/fileio/i-o-completion-ports,它提到使用带有预分配线程池的 I/O 完成端口,但我找不到在哪里可以分配线程数

在哪里可以分配线程池中的线程数? 谁能在这里帮我?多谢!

首先是有关 I/O 完成端口 (IOCP( 和线程池的一般信息。 我们这里有 2 个选项:


全部自行创建:

通过CreateIoCompletionPort(或NtCreateIoCompletion(自行创建IOCP

。通过自行创建线程,这些线程将被调用GetQueuedCompletionStatus(或NtRemoveIoCompletion(。

您需要的每个文件都通过具有FileCompletionInformationFILE_COMPLETION_INFORMATIONNtSetInformationFile或通过CreateIoCompletionPort(此Win32 API结合了NtCreateIoCompletionNtSetInformationFile的功能(再次绑定到IOCP


使用系统IOCP线程池

系统(NTDLL.dll(在进程启动时创建默认线程池(现在名为TppPoolpGlobalPool(。 您对此池具有周控制。 你不能直接得到它 指针PTP_POOL. 存在未记录的TpSetDefaultPoolMaxThreads(用于设置此池中的最大线程数(,但对于最小值为否。

如果需要 - 您可以通过CreateThreadpool函数创建其他线程池。

创建新线程池后,您可以(但不应该!(调用SetThreadpoolThreadMaximum来指定池可以分配的最大线程数,SetThreadpoolThreadMinimum指定池中可用的最小线程数。

线程池维护 I/O 完成端口。 在调用CreateThreadpool中创建的IOCP- 我们无法直接访问它。

所以最初在进程中存在一个全局/默认线程池(TppPoolpGlobalPool(和iocp(Windows 10 用于并行加载程序创建其他线程池LdrpThreadPool,但这当然仅供内部使用 - 当DDL加载时(

最后,通过调用CreateThreadpoolIo将自身文件绑定到IOCP

请注意,MSDN 文档在这里是错误的 -

创建新的 I/O 完成对象。

实际上CreateThreadpoolIo函数不会创建新的I/O完成对象 - 它仅在调用CreateThreadpool中创建。 此 API 将文件(不是句柄而是文件!(绑定到与池关联的 I/O 完成对象。 到哪个池? 查找最后一个参数 - 指向TP_CALLBACK_ENVIRON的可选指针。

你可以用下一种方式指定一个线程池 - 分配回调环境,为其调用InitializeThreadpoolEnvironment,然后SetThreadpoolCallbackPool

如果不指定线程池,则全局线程池将在调用CreateThreadpoolIo中使用 - 因此文件将绑定到默认/全局进程iocp

在这种情况下,您不需要通过自调用GetQueuedCompletionStatus(或NtRemoveIoCompletion(来执行此操作 - 系统从池中为您执行此操作。 然后调用您的IoCompletionCallback回调函数,您将该函数传递给内部的系统CreateThreadpoolIo调用


我们还可以通过BindIoCompletionCallback(RtlSetIoCompletionCallback( - 它将全局 (TppPoolpGlobalPool( 线程池拥有的 I/O 完成端口与指定的文件句柄相关联。这是旧的 API 和案例 2 的变体。在这里我们不能使用自定义轮询 - 只能处理全局。


现在让我们回到具体的Python代码。 它使用哪种情况? 是自己创建IOCP和线程池吗? 还是使用系统线程池? 如果使用系统 - 使用它全局或由CreateThreadpool分配的自定义线程池? 如果你不知道这一点 - 这里什么也做不了。 即使知道..或者这个库有特殊的API/接口(或者在Python中如何调用(来控制这个(以防自我或自定义池已使用(,或者您只能按原样使用。并且很难确定池中真正需要多少线程