首页 > 解决方案 > 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() 

这个简单的示例脚本运行良好并返回类似: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()。但是,如果我将类Test2放在与 (# Script1.py ) 相同的脚本中,Test那么一切都很好。

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

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

标签: pythonmultithreadingmultiprocessingspyder

解决方案


首先,在Script1.py中,您放置if __name__ == "__main__":检查的位置不正确。应按如下方式放置:

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

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

但是像我所做的那样移动检查的真正原因是,您只想在将Script1.pyTest([2, 3]).run()作为“主”脚本执行时执行该语句,而不是在其他脚本导入它时执行该语句。通过像我所做的那样放置检查,当它被导入时,它的名称将不再是“__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__":around 这样的语句(__name__在新创建的进程中不会是“__main__”)。所以不需要在run方法中进行这样的检查,它不在全局范围内。但最终,无论您运行什么脚本开始,您需要检查全局范围内的任何代码,这些代码创建一个进程或调用一个创建进程的函数或方法。

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


推荐阅读