根据我的理解,multiprocessing
在Linux上使用fork
,这意味着multiprocessing
创建的每个进程都有自己的内存空间,并且内部进行的任何更改都不会影响其他fork
ed进程。
但我遇到了这种相当奇怪的情况:
import multiprocessing
i = -1
def change(j):
global i
print(i, end=" ") # should print -1
i = j
with multiprocessing.Pool(20) as p:
p.map(change, range(20))
print(i) # should print -1
我认为这个程序将打印正好21个-1
,因为multiprocessing
创建了20个独立的子进程,其内存空间不共享,这意味着行i = j
不会影响任何其他进程中i
的值;因此在印刷时i = -1
。
然而,该程序实际上打印了-1
和0到19之间随机数量的混合。
例:
-1 -1 -1 -1 -1 4 -1 5 -1 6 -1 8 -1 -1 14 -1 -1 12 -1 -1 -1
所以我的问题是,为什么我没有得到21个-1
?
Python 3.2引入了maxtasksperchild。
Maxtasksperchild是工作进程在退出之前可以完成的任务数,并替换为新的工作进程,以释放未使用的资源。默认的maxtasksperchild是None,这意味着工作进程将与池一样长。
import multiprocessing
i = -1
def change(j):
global i
print(i, end=" ") # should print -1
i = j
if __name__ == '__main__':
with multiprocessing.Pool(20, maxtasksperchild=1) as p:
p.map(change, range(20))
print(i) # should print -1
Multiprocessing.Pool不保证每个任务都将在新进程中运行。实际上,您使用multiprocessing.Pool的原因是用于创建新进程的成本被认为是昂贵的任务,因此您希望使用进程池来避免该进程创建开销。多处理的典型使用模式.Pool是您创建了大量任务,然后创建了一个包含少量工作人员的池(通常取决于您的计算机具有的CPU核心数),池将计划任务给工作人员和尽可能重用过程。如果要始终启动新进程,则应使用multiprocessing.Process。
这是一个常见的误解,它不会,但Pool(20)
将立即创建20个流程。事实上,即使处理程序线程开始之前,这些进程都将启动,这将把任务提供给inqueue
,以便稍后由工作人员处理。
这些过程运行multiprocessing.pool.worker
代码,直到他们从.get()
来到inqueue
。只是并非所有人都需要重新安排在短时间内从共享队列中获取任务。队列读取是顺序的,一次只能有一个进程从中读取。有些进程会发生多个任务而其他进程没有进行调度,因为您的操作系统会在内核上运行不同的操作。当您看到除-1
之外的其他值时,进程会在此处获得多个任务。