在Python 3.8中,concurrent.futures.ProcessPoolExecutor
已更新,将Windows上可以使用的最大工作器(进程)数量限制为61。出于原因,请参阅此和此,但据我了解:
- 在Windows上,
multiprocessing
调用Windows API函数WaitForMultipleObjects
,用于等待进程完成。它最多可以等待 63 个对象,减去结果队列读取器和线程唤醒读取器,因此限制为 61 个。(即Windows使用每个进程的线程来跟踪进程)。
(另请参阅此 SO 问题)
然而,multiprocessing
仍然使用os.cpu_count()
。它一开始会抛出一个Value Error
,但随后继续并使用我 100% 的 CPU 内核。例如
Exception in thread Thread-N:
Traceback (most recent call last):
File "C:UsersusernameAppDataLocalProgramsPythonPython38libthreading.py", line 932, in _bootstrap_inner
self.run()
File "C:UsersusernameAppDataLocalProgramsPythonPython38libthreading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "C:UsersusernameAppDataLocalProgramsPythonPython38libmultiprocessingpool.py", line 519, in _handle_workers
cls._wait_for_updates(current_sentinels, change_notifier)
File "C:UsersusernameAppDataLocalProgramsPythonPython38libmultiprocessingpool.py", line 499, in _wait_for_updates
wait(sentinels, timeout=timeout)
File "C:UsersusernameAppDataLocalProgramsPythonPython38libmultiprocessingconnection.py", line 879, in wait
ready_handles = _exhaustive_wait(waithandle_to_obj.keys(), timeout)
File "C:UsersusernameAppDataLocalProgramsPythonPython38libmultiprocessingconnection.py", line 811, in _exhaustive_wait
res = _winapi.WaitForMultipleObjects(L, False, timeout)
ValueError: need at most 63 handles, got a sequence of length 98
我的机器有 96 个内核。这个"错误"真的是错误吗?如果没有,我应该只使用multiprocessing
模块而不是concurrent.futures
模块,这会将我的 CPU 使用率限制为 61 个内核?
编辑:我怀疑这是一个错误,因为我认为multiprocess
将继续等待引发错误的过程完成。如果我不限制内核数量,这似乎会发生(程序在 CPU 使用率下降后挂起)。但是,我不确定是否真的是。
你的问题很好。查看代码,似乎这将是一个不可恢复的错误。但在我看来,ThreadPoolExecutor
中会有代码将 Windows 下的池大小限制为 61,而不是对multiprocessing.Pool
类强制执行。无论如何,使用以下程序进行检查应该很容易。如果它没有打印完成!并挂起,我会说肯定有问题,如果你使用multiprocessing.Pool
,你应该明确限制池的大小:
import multiprocessing
def worker(x):
return x ** 2
def main():
pool = multiprocessing.Pool(96)
results = pool.map(worker, range(96))
assert len(results) == 96
pool.close()
pool.join()
print('Done!')
if __name__ == '__main__':
main()
但是您的程序挂起的事实相当确定上述程序将挂起,我怀疑您甚至不会得到assert
声明。无论哪种方式,使用大于 61 的池大小都不可靠。