我是一般的并行化和特别是concurrent.futures的新手。我想对我的脚本进行基准测试并比较使用线程和进程之间的差异,但我发现我甚至无法运行,因为在使用ProcessPoolExecutor
时我无法使用我的全局变量。
以下代码会按照我的预期输出Hello
,但当您更改ThreadPoolExecutor
的{{1}}时,会输出ProcessPoolExecutor
。
None
我不明白为什么会这样。在我的真实程序中,init用于将全局变量设置为CLI参数,并且有很多。因此,似乎不建议将它们作为参数传递。那么如何正确地将这些全局变量传递给每个进程/线程呢?
我知道我可以改变一切,这会有效,但我不明白为什么。例如。以下适用于两个Executors,但它也意味着必须为每个实例进行全局初始化。
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
greeting = None
def process():
print(greeting)
return None
def main():
with ThreadPoolExecutor(max_workers=1) as executor:
executor.submit(process)
return None
def init():
global greeting
greeting = 'Hello'
return None
if __name__ == '__main__':
init()
main()
所以我的主要问题是,实际发生的事情。为什么这段代码适用于线程,而不适用于进程?而且,如何正确地将set globals传递给每个进程/线程,而不必为每个实例重新初始化它们?
(旁注:因为我已经读过在Windows上,concurrent.futures可能表现不同,我必须注意到我在Windows 10 64位上运行Python 3.6。)
答案 0 :(得分:1)
实际上,OP的第一个代码将在Linux上按预期工作(在Python 3.6-3.8中进行了测试),因为
在Unix上,子进程可以利用在操作系统中创建的共享资源。 使用全局资源的父流程。
,如多处理doc中所述。但是,由于一个神秘的原因,它无法在运行Mojave的Mac上运行(应该是UNIX兼容的OS;仅在Python 3.8上进行了测试)。并且可以肯定的是,它在Windows上不起作用,通常不建议将其用于多个进程。
答案 1 :(得分:0)
我不确定这种方法的局限性,但您可以在主进程/线程之间传递(可序列化的?)对象。这也可以帮助你摆脱对全球变量的依赖:
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
def process(opts):
opts["process"] = "got here"
print("In process():", opts)
return None
def main(opts):
opts["main"] = "got here"
executor = [ProcessPoolExecutor, ThreadPoolExecutor][1]
with executor(max_workers=1) as executor:
executor.submit(process, opts)
return None
def init(opts): # Gather CLI opts and populate dict
opts["init"] = "got here"
return None
if __name__ == '__main__':
cli_opts = {"__main__": "got here"} # Initialize dict
init(cli_opts) # Populate dict
main(cli_opts) # Use dict
适用于两种执行者类型。
编辑:即使听起来它对您的用例来说不是一个问题,但我会指出ProcessPoolExecutor
,opts
dict你进入process
dict {1}}将是一个冻结的副本,因此在进程中不会看到突变,也不会在返回__main__
块后看到它们。另一方面,ThreadPoolExecutor
将在线程之间共享dict对象。
答案 2 :(得分:0)
让图像一个进程是一个框,而一个线程是一个框内的工作者。工作人员只能访问框中的资源,不能触及其他框中的其他资源。
因此,当您使用线程时,您将为当前框(主进程)创建多个工作线程。但是当你使用进程时,你正在创建另一个框。在这种情况下,此框中初始化的全局变量与另一个框中的全局变量完全不同。这就是为什么它没有像你期望的那样工作的原因。
jedwards提供的解决方案在大多数情况下都足够好。您可以将资源打包在当前框中(序列化变量)并将其传递到另一个框(传输到另一个进程),以便该框中的工作人员可以访问资源。
答案 3 :(得分:0)
进程表示在主要进程中运行线程时,在术语的OS含义中在单独进程中运行的活动。每个进程都有自己独特的命名空间。
您的主进程通过在greeting
条件中为其自己的命名空间调用init()
将值设置为__name__ == '__main__'
。在您的新流程中,这不会发生(此处__name__
为'__mp_name__'
)因此greeting
仍为无,并且init()
永远不会被实际调用,除非您在函数中明确地执行此操作过程执行。
虽然通常不建议在进程之间共享状态,但有一些方法可以这样做,如@jedwards回答中所述。
您可能还想查看文档中的Sharing State Between Processes。