有些文章告诉我,在创建一个新的子进程时,操作系统几乎会复制来自partent的所有数据,包括进程的struct,stack,heap等。因此,我认为可以将全局变量,静态变量复制到子进程中,在调用fork()时,其内容等于父级的值。但是遵循python代码的结果使我感到困惑:
from multiprocessing import Process
ids = []
ids.extend([1, 2, 3, 4])
def worker(sub_id):
global ids
print("sub_id=%s, the content of ids: [%s]" % (sub_id, ",".join(["%s" % x for x in ids])))
def init():
global ids
ids.append(-100)
def main():
init()
sub_process = list()
for i in range(2):
process = Process(target=worker, args=(i, ))
process.start()
sub_process.append(process)
for p in sub_process:
p.join()
global ids
ids.append(100)
print("the main process, the content of ids: [%s]" % (",".join(["%s" % x for x in ids])))
if __name__ == "__main__":
main()
以上代码的执行结果:
sub_id=0, the content of ids: [1,2,3,4]
sub_id=1, the content of ids: [1,2,3,4]
the main process, the content of ids: [1,2,3,4,-100,100]
我期望的结果:
sub_id=0, the content of ids: [1,2,3,4, -100]
sub_id=1, the content of ids: [1,2,3,4, -100]
the main process, the content of ids: [1,2,3,4,-100,100]
我不知道为什么函数ids
中init()
的更改不会复制到子进程,而全局节ids.extend([1, 2, 3, 4])
中的更改对子进程可见。
感谢您的每次答复。
答案 0 :(得分:1)
如文档中所述,multiprocessing
具有三种不同的启动过程的方式。主要的两个是fork
和spawn
。 1
fork
复制您的父进程。您对此事一无所知: 2 孩子从父母全局变量的副本开始,依此类推。
spawn
创建一个全新的过程,启动Python解释器,并import
来创建您的模块。
在Unix上,默认为fork
,但可以选择spawn
和forkserver
。在Windows上,spawn
是默认选项,也是唯一的选项,因为Windows不提供fork
API。
由于您使用的是Windows,因此子级不会从父级那里获得ids
的副本,但是ids = []
和ids.extend(…)
代码会在import
上运行,因此无论如何它们最终都具有相等的值。但是受__main__
保护的任何代码都不会被import
运行,因此它们不会调用main
,因此不会init
,也不会{{1} }。
ids.append(-100)
库的设计使您可以在所有平台上使用相同的方式来使用它。 3 {{3} }部分,但基本思想是:不要假设全局变量已被复制,或者没有被复制。
这意味着,除了multiprocessing
,__main__
和import
语句之外,顶层def
防护层之外通常不会有任何代码,也许一些简单的全局常量分配。
您需要做的任何复杂的设置,都在每个子进程中完成。 4 您想在进程之间共享的任何内容(例如class
或Lock
) ,您可以在Queue
防护中创建并传递给子级作为参数。
1。对于__main__
,请参阅文档以了解详细信息;它主要用于那些使用库的程序,这些程序希望对无法与forkserver
配合使用的线程做一些花哨的事情,这在macOS上很常见,尽管它在其他情况下也很有用。
2。诸如打开文件之类的东西有些复杂,但在这里并不重要。
3。您可以可以指定fork
,然后将每个平台都视为Windows。但是在某些Unix平台上,spawn
进程的速度可能很慢。另外,在Unix上让共享文件之类的东西工作可能会很痛苦,因此有时编写与spawn
或fork
兼容的代码比编写与spawn
兼容的代码更容易。 Unix或Windows上的spawn
。但是,在Unix上使用spawn
和在Windows上使用forkserver
可能是一个很好的折衷方案/
4。如果您使用的是spawn
,请使用Pool
函数。