Windows 的多处理问题

multiprocessing issue with Windows

在 Windows 中使用多处理时,我们必须有 if __name__ == '__main__':。例如:

# Script1.py

import multiprocessing
class Test(object):
    def __init__(self, x):
        self.x = x

    def square(self, i, return_jobs, x):
        i = x**2
        return_jobs[i] = i 

    def run(self):
        if __name__ == "__main__":
            manager = multiprocessing.Manager()
            return_jobs = manager.dict()
            jobs = []
            for i in range(len(self.x)):
                p = multiprocessing.Process(target = self.square, args=(i, return_jobs , self.x[i]) )
                jobs.append(p)
                p.start()
            for proc in jobs:
                print(proc)
                proc.join()
            print('result',return_jobs.values())

Test([2, 3]) .run() 

这个简单的示例脚本 运行 很好并且 return 类似于:result [4, 9]。但是,如果我有一个不同的脚本并导入 Script1 并使用 Test 那么它将不起作用。也就是说,

# Script2.py

from Script1.py import Test 
class Test2(object):
    def __init__(self, y):
        self.y = y
        
    def run(self):
        z = Test(self.y).run()

根本不会调用函数Test(self.y).run()。但是,如果我将 class Test2 放在与 Test (# Script1.py ) 相同的脚本中,那么一切都很好。

解决此问题的最佳方法是什么? Script1.py 是整个代码的子流程。我不想将这些脚本组合在一起...

我还应该注意,我也在使用 Spyder。这可能是个问题。

首先,在 Script1.py 中,您放置 if __name__ == "__main__": 检查的位置不正确。应该这样放置:

if __name__ == "__main__":
    Test([2, 3]) .run() 

这有两个原因。首先,当创建新进程时,全局范围内的任何语句都将由这些进程执行。如果您没有像我上面那样进行检查,您将不必要地创建 Test 对象的实例。的确,当针对这些对象调用 run 时,run 会立即 return 因为您确实在何处放置了检查,但为什么要创建对象开始?

但是像我这样移动检查的真正原因是你只想在执行 Script1.py 时执行语句 Test([2, 3]).run()作为“主”脚本,而不是当它被其他脚本导入时。通过像我一样放置支票,当它被导入时,它的名称将 not 不再是“__main__”,因此该语句将不会被执行,这给了你更多灵活性。

这现在允许您在 Script2.py 添加您自己的 if __name__ == '__main__': 检查如下:

from Script1 import Test

class Test2(object):
    def __init__(self, y):
        self.y = y

    def run(self):
        z = Test(self.y).run()

if __name__ == '__main__':
    Test2([3, 6]).run()

打印:

<Process name='Process-2' pid=9200 parent=4492 started>
<Process name='Process-3' pid=16428 parent=4492 started>
result [9, 36]

因此,当 Script2.py 是正在执行的“主要”脚本时,您可以控制创建的对象和 运行.

说明

使用 Windows 时要记住的重要一点是,当脚本启动一个新进程时,该进程从顶部开始执行源代码,因此全局范围内的所有语句(导入语句、函数声明、变量赋值)等)被执行。因此,您希望避免在全局范围内拥有不需要的东西,因为它们将由新进程重新执行,并且您可能正在进行计算或创建大型数据结构,而新创建的进程不使用并且您白白浪费了 CPU 个周期或内存。但是你绝对不能在全局范围内有任何语句,这些语句在执行时最终会递归地重新创建你刚刚创建的进程。这就是为什么我们需要 if __name__ == "__main__": 围绕这样的语句(__name__ 在新创建的进程中不会是“__main__”)。所以不需要在 run 方法中进行这样的检查,它不在全局范围内。但最终,无论您 运行 开始什么脚本,您 都将 需要检查全局范围代码中创建进程或调用创建进程的函数或方法的任何代码一个过程。

注意当Script2.py导入Script1.py,Script1.py 现在是一个模块,它的 __name__ 值将是“Script1”,代码 Test([2, 3]).run() 也不会执行。所以这也解释了为什么当我们创建一个模块时,我们可以将测试代码放在一个 if __name__ == "__main__": 块中——它不会在导入模块时执行。