我试图了解以下代码为何如此缓慢:
import threading
import time
import concurrent.futures
from datetime import datetime
def dump(txt):
print(f'[{datetime.now()}] ({threading.get_ident():05}) {txt}\n', end='')
def sleep_(_):
dump('Start')
time.sleep(0.1)
dump('Stop')
def main(n=10, processes=10):
dump('before with')
with concurrent.futures.ProcessPoolExecutor(processes) as pool:
dump('before map')
tmp = list(pool.map(sleep_, range(n)))
dump('after map')
dump('after with')
if __name__ == '__main__':
main()
这是结果:
[2020-10-17 23:34:12.822813] (07100) before with
[2020-10-17 23:34:12.824808] (07100) before map
[2020-10-17 23:34:21.409045] (14100) Start
[2020-10-17 23:34:21.414031] (15408) Start
[2020-10-17 23:34:21.414031] (20292) Start
[2020-10-17 23:34:21.415029] (18972) Start
[2020-10-17 23:34:21.416026] (13660) Start
[2020-10-17 23:34:21.416026] (10904) Start
[2020-10-17 23:34:21.417023] (18828) Start
[2020-10-17 23:34:21.418021] (18616) Start
[2020-10-17 23:34:21.504788] (01776) Start
[2020-10-17 23:34:21.509775] (14100) Stop
[2020-10-17 23:34:21.509775] (14100) Start
[2020-10-17 23:34:21.514761] (20292) Stop
[2020-10-17 23:34:21.514761] (15408) Stop
[2020-10-17 23:34:21.515760] (18972) Stop
[2020-10-17 23:34:21.516757] (13660) Stop
[2020-10-17 23:34:21.516757] (10904) Stop
[2020-10-17 23:34:21.517754] (18828) Stop
[2020-10-17 23:34:21.518751] (18616) Stop
[2020-10-17 23:34:21.605519] (01776) Stop
[2020-10-17 23:34:21.610506] (14100) Stop
[2020-10-17 23:34:21.611503] (07100) after map
[2020-10-17 23:34:23.281562] (07100) after with
我想在这里理解的是为什么要花近9秒钟才能启动第一个过程。为什么要花近2秒钟来清理它们?
这是Windows系统(正在调试)。 当我正常运行它时,向上旋转和向下滚动将花费+-0.5s。
与在同一系统上的Debian WSL相比:旋转0.04秒,结束时0.006s。
这是正常行为吗?和/或如何对其进行改进?为什么会这样?
谢谢!
答案 0 :(得分:0)
Windows没有等效于fork
系统调用来复制进程。这意味着在Windows中使用具有10个工作进程的多处理池将创建10个新 python进程。
启动第一个进程不需要9秒,启动池中的所有10个进程然后将目标函数激发到第一个进程需要9秒。
答案 1 :(得分:0)
在Linux和其他类似Unix的操作系统上,进程是通过fork
系统调用创建的,该系统调用基本上会创建主进程的惰性副本(写时复制)。这是一个快速的操作。
Windows没有fork
功能,因此必须通过启动新的解释器并执行完整的Python代码(末尾受保护的if __name__ == '__main__':
部分除外)来创建每个工作进程,以在建立进程之前可以使用。 Python在multiprocessing docs中将此方法称为“生成”方法。
自Python 3.8起,spawn方法也是MacOS上的默认设置。