if __name__ == "__main__":' 后面的代码不执行

4 投票
2 回答
3794 浏览
提问于 2025-04-17 12:23

所以,我的情况是这样的。

我在Windows上使用Eclipse里的PyDev,Python解释器版本是2.7.2。

我在尝试使用内置的多进程库,想要分叉出很多进程来并行处理一个计算量很大的循环。网上的教程告诉我要使用:

if __name__ == "__main__":

这样可以防止它生成几乎无限的进程,导致我的系统崩溃。

问题是,我是在一个模块里调用这个,而不是在我的主脚本中;所以,后面的代码根本不会执行。根本没有并行处理的机会。当然,如果我把它去掉,就会出现无限生成进程的情况,搞得运行代码的机器瘫痪。

为了参考,这里是相关的代码:

from tribe import DataCache
from tribe import WorldThread
from tribe import Actor
from time import sleep
import multiprocessing

class World:
def __init__(self,numThreads,numActors,tickRate):
    print "Initalizing world..."
    self.cache = DataCache.DataCache()
    self.numThreads = numThreads
    self.numActors = numActors
    self.tickRate = tickRate
    self.actors = []
    self.processes = []
    for i in range(numActors):
        self.actors.append(Actor.Actor("test.xml",self.cache))
    print "Actors loaded."
def start_world(self):
    print "Starting world"
    run_world = True;
    while run_world:
        self.world_tick()
        sleep(2)

def world_tick(self):
        if __name__ == '__main__':
            print "World tick"
            actor_chunk = len(self.actors)/self.numThreads
            if len(self.processes)==0:
                for _ in range(self.numThreads):
                    new_process = multiprocessing.Process(WorldThread.WorldProcess.work, args=(_, self.actors[_*actor_chunk,(_+1)*actor_chunk]))

还有它调用的类:

class WorldProcess():
def __init__(self):
    print "World process initilized."
    ''' Really, I'm not sure what kind of setup we'll be doing here yet. '''
def work(self, process_number, actors):
    print "World process" + str(process_number) + " running."
    for actor in actors:
        actor.tick()
    print "World process" + str(process_number) + " completed."

我是不是可以理解为,整个 if name == "main": 的检查只有在可执行脚本里才有效?如果是这样的话,我该如何安全地在模块中分叉出进程?如果不是,那为什么在这里不管用呢?

2 个回答

2

要控制进程的数量,可以使用来自 multiprocessingPool 类:

from multiprocessing import Pool
p = Pool(5)
def f(x):
     return x*x
p.map(f, [1,2,3])

(编辑:根据评论,这只是关于 Pool 类的使用方法查看更多

使用 __name__ 不是必须的,因为你已经明确地把要运行的 实际 Python 函数 传给了 Process

这样的写法:

def world_tick(self):
    if __name__ == '__main__':
        print "World tick"
        actor_chunk = len(self.actors)/self.numThreads
        if len(self.processes)==0:
            for _ in range(self.numThreads):
                new_process = multiprocessing.Process(WorldThread.WorldProcess.work, args=(_, self.actors[_*actor_chunk,(_+1)*actor_chunk]))

是非常糟糕的。要简化它。

更好的写法是:

class WorkArgs(object):
    ... many attributes follow ...

def proc_work(world_thread, work_args):
    world_thread.WorldProcess.work(work_args.a, work_args.b, ... etc)

p = Pool(5)
p.map(proc_work, [(world_thread, args0), (world_thread, args1), ...])

希望这对你有帮助!

另外,序列化你的参数并传递给其他进程会导致导入你的模块。因此,最好确保你的模块在没有被告知的情况下不会执行任何分叉/魔法/工作(例如,只包含函数/类定义或 __name__ 的魔法,而不是实际的代码块)。

2

把这个作为一个回答,因为它在评论里提到过:

if __name__ == "__main__" 是你在一个脚本的最外层写的代码,这个脚本将作为程序的入口点。它的作用是确保只有在直接运行这个脚本时,里面的代码才会被执行。

如果你有一个作为入口点的脚本,你就需要写这个 name == main 的判断。而在一个你想要进行多进程处理的模块里,你只需要像启动线程那样循环并启动你的进程。

撰写回答