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

时间:2018-06-28 12:40:34

标签: python python-import python-multiprocessing python-module

我正在尝试在C类的构造函数中导入mymodule.py,以便在使用多处理方法将f方法调用为新过程时,C类的每个新实例使用其自己的mymodule。模块。

请查看以下代码:

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后,标准输出为:

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的suggestion解决了该问题:

代码更正

    # 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)的最新结果的第一列始终是不同的!

2 个答案:

答案 0 :(得分:1)

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

在第一种情况和第二种情况下获得不同结果的原因是,在创建C实例后,您立即产生了一个进程(因此派生/拆分了上下文),而您并没有为此付出很多第二种方法,首先实例化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())。如果您在保留缓存的模块时只想获得不同的结果(即无需重新评估),则将模块逻辑放入函数中,然后执行该函数。

答案 1 :(得分:0)

例如,我相信您在Unix系统(例如Linux)上,用于创建子进程的默认策略是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()