Python并发进程PoolExecutor和全局变量:在Linux上工作,在MacOS上出错
下面的代码示例在两台Linux机器上按我的想法运行:在运行Red Hat 4.8.5-39内核的大型CentOS服务器上使用Python 3.6.8,在运行Debian 8.3.0-6内核的基于MX的机器上使用Python 3.7.3)Python并发进程PoolExecutor和全局变量:在Linux上工作,在MacOS上出错,python,linux,macos,global-variables,concurrent.futures,Python,Linux,Macos,Global Variables,Concurrent.futures,下面的代码示例在两台Linux机器上按我的想法运行:在运行Red Hat 4.8.5-39内核的大型CentOS服务器上使用Python 3.6.8,在运行Debian 8.3.0-6内核的基于MX的机器上使用Python 3.7.3) $python3 testshared.py filename.dat filename.dat 270623586670000 但是,在运行Mojave 10.14.6的Mac上,使用Python 3.8.3,我得到一个错误,因为函数processBigFa
$python3 testshared.py filename.dat
filename.dat
270623586670000
但是,在运行Mojave 10.14.6的Mac上,使用Python 3.8.3,我得到一个错误,因为函数processBigFatRow()
中的foo=[]
。请注意,foo
是在启动进程池之前在getbigfata()
中分配的。就像在Linux中一样,在getbigfata()
中分配的foo
版本被传递给进程,而在Mac上,进程只使用代码顶部的初始化(我必须将其放在那里,这样它们才是全局
变量)
我知道流程是主流程的“独立副本”,您不能在一个流程中分配全局变量,而期望它们在另一个流程中发生变化。但是,对于在并行进程启动之前已经设置好的、仅用于引用的变量,该怎么办呢?就好像整个OSs的过程拷贝是不一样的。哪一个“按设计”工作
代码示例:
将pylab作为pl导入
从同步进口期货
导入系统
foo=[]
bar=[]
def getBigFatData(文件名):
全球足球俱乐部
#获取大数据
打印(文件名)
foo=pl.arange(1000000)。重塑(10001000)
#计算结果
bar=pl.sum(foo,轴=1)
def PROCESS BIGFATROW(行):
总计=pl.sum(foo[行,:]**2),如果行%5,则为条[行]
返回总数
def main():
getBigFatData(sys.argv[1])
总计=0。
行=pl.arange(100)
使用futures.ProcessPoolExecutor()作为池:
对于pool.map中的tot(processBigFatRow,行):
总计+=总计
打印(总计)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
main()
编辑:
正如所建议的那样,我在MX Linux机器上测试了Python 3.8.6,它可以正常工作
因此,它可以在Linux上使用Python 3.6.8、3.7.3和3.8.6。
但在Mac上使用Python3.8.3时,它不起作用
编辑2:
发件人:
在Unix上,子进程可以使用在使用全局资源的父进程中创建的共享资源
因此,它在Windows上不起作用(这不是最佳实践),但在Mac上不起作用吗?您正在比较两个不同python版本中相同代码的输出。内置模块可能是相同的,也可能在3.6和3.8之间发生了重大变化。在继续之前,您应该在两个地方的相同python版本上运行代码。这是因为,在MacOS上,python 3.8中的。它从
fork
(py37)到spawn
(py38),造成了相当大的咬牙份额
在版本3.8中更改:在macOS上,spawn
start方法现在是
违约fork
start方法应视为不安全的
导致子流程崩溃。看见
使用spawn
:全局变量不与多进程进程共享
因此,实际上,作为一个快速修复方法,在所有的调用中使用指定一个'fork'
上下文。但是要注意上面的警告;一个长期的解决方案是通过使用多处理上列出的技术之一共享变量
例如,在上面的代码中,替换:
with ProcessPoolExecutor() as pool:
...
与:
备选方案:
当您正在编写一两个小脚本,并且确信没有人在某处使用不同的main
调用您的代码时,您可以在main
代码块中一次性设置默认的启动方法,方法是:
但一般来说,我更喜欢第一种方法,因为您不必假设调用方事先设置了start方法。根据文件:
请注意,最多应该调用一次,并且应该
在main的子句的中受保护
模块
为我工作,谢谢!什么是
mp
?名称mp
未定义错误。我正在使用import concurrent.futures
作为导入语句?谢谢。@Sid:我编辑了答案;您需要导入多处理为mp
(您仍然可以按原样使用其余代码,包括使用并发.futures
)。谢谢@PierreD,您的回答让我明白了这一点。
import multiprocessing as mp
with ProcessPoolExecutor(mp_context=mp.get_context('fork')) as executor:
...
if __name__ == '__main__':
mp.set_start_method('fork')
...