无法在同一进程中多次使用 imp.load_module 加载同一模块

Cannot load the same module, in the same process, multiple times using imp.load_module

我试图在 C class 的构造函数中导入 mymodule.py 以便 C class 的每个新实例在 f 方法被称为使用多处理模块的新进程。

请查看以下代码:

mymodule.py

import random
n = random.randint(0, 1000)

mp_test.py

import time
import multiprocessing as mp
import imp

class C(object):

    def __init__(self, c='*'):
        self.mm=imp.load_module('mm', *imp.find_module('mymodule'))
        self.c = c

    def f(self):
        print self.c, id(self.mm), id(self.mm.n), self.mm.n

def main():

    p = []
    for _ in range(2):
        p.append(mp.Process(target=C().f))
        p[-1].start()

    time.sleep(1)
    print ''

    p0 = mp.Process(target=C(c='>').f)
    p1 = mp.Process(target=C(c='>').f)
    p0.start()
    p1.start()

if __name__ == '__main__':
    main()

执行 mp_test.py 后,stdout 结果为:

funk@linux:~/mp_test# python mp_test.py
* 140679674677800 94828316092856 40
* 140679674677800 94828316835224 486

> 140679674677800 94828317211688 763
> 140679674677800 94828317211688 763

正如预期的那样,以下代码创建了 mymodule 的不同实例,因此生成了两个不同的随机数:

    p = []
    for _ in range(2):
        p.append(mp.Process(target=C().f))
        p[-1].start()

* 140679674677800 94828316092856 40
* 140679674677800 94828316835224 486

start() 方法被如下调用时 mymodule 没有导入两次,因此随机数是相同的...

    p0 = mp.Process(target=C(c='>').f)
    p1 = mp.Process(target=C(c='>').f)
    p0.start()
    p1.start()

> 140679674677800 94828317211688 763
> 140679674677800 94828317211688 763

请提出您的想法...!

编辑

zwer 的 解决了这个问题:

代码更正

    # ERROR self.mm=imp.load_module('mm', *imp.find_module('mymodule'))
    self.mm=imp.load_module('mm_' + str(uuid.uuid4()), *imp.find_module('mymodule'))

更新结果

funk@linux:~/mp_test# python -O mp_test.py
* 139813126327944 94540189500648 396
* 139813126447184 94540189500504 491

> 139813126447296 94540189500432 847
> 139813126447240 94540189500384 389

注意代表id(self.mm)的最新结果的第一列总是不同的!

我相信你在像 Linux 这样的 Unix 系统上,例如,创建子进程的默认策略是 forking
子进程继承其父进程的执行状态和内存状态,包括导入的 Python 模块。你说 mymodule 不是第二次导入是正确的。

您实际上想用一个函数将代码包装在 mymodule 中:

import random

def get_random():
    return random.randint(0, 1000)

然后从您的 mp_test.C.f() 方法中调用它:

def f(self):
    print self.c, id(self.mm), id(self.mm.get_random()), self.mm.get_random()

您的问题与多处理没有直接关联,您在尝试将 运行 f() 方法作为单独的进程之前在同一进程中加载​​模块。这会导致模块缓存,因此下次您尝试导入它时,Python 会为您提供一个缓存实例,其中随机部分不会再次执行。

为什么你在第一种情况和第二种情况之间得到不同的结果是因为你在创建 C 实例之后立即生成了一个进程(因此 fork/split 上下文),而你没有为你的第二种方法提供这种奢侈,你首先实例化你的 C 实例然后开始新的进程 - 例如如果你要这样做:

p0 = mp.Process(target=C(c='>').f)
p0.start()
p1 = mp.Process(target=C(c='>').f)
p1.start()

你也会得到预期的结果。

也就是说,如果你想在同一个进程中多次加载同一个模块,同时避免缓存,你必须在加载时给它一个不同的名称,比如:

class C(object):

    load_counter = 0

    def __init__(self, c='*'):
        self.mm=imp.load_module('mm' + str(C.load_counter), *imp.find_module('mymodule'))
        C.load_counter += 1
        self.c = c

或者在派生进程中加载​​模块( 调用start() 之后)。如果您只想在保留缓存模块的同时获得不同的结果(即无需重新评估)- 将模块逻辑放在一个函数中,然后执行该函数。