Python并发进程PoolExecutor和全局变量:在Linux上工作,在MacOS上出错

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

下面的代码示例在两台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,我得到一个错误,因为函数
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')
    ...