Невозможно загрузить один и тот же модуль в одном и том же процессе несколько раз, используя imp.load_module - PullRequest
0 голосов
/ 28 июня 2018

Я пытаюсь импортировать mymodule.py в конструктор класса C, чтобы каждый новый экземпляр класса C использовал свой собственный модуль, когда метод 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

Обратите внимание , что 1-й столбец последних результатов, представляющий id(self.mm), всегда отличается!

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Ваша проблема не связана напрямую с многопроцессорностью, вы выполняете загрузку модуля в том же процессе, прежде чем пытаться запустить метод 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() для него). Если вы просто хотите получить другой результат при сохранении кэшированного модуля (т. Е. Без переоценки) - поместите логику модуля в функцию и затем вместо нее выполните эту функцию.

0 голосов
/ 28 июня 2018

Я полагаю, что вы работаете в Unix-системе, например, в Linux, где стратегией по умолчанию для создания подпроцесса является разветвление .
Подпроцесс наследует состояние выполнения и состояние памяти своего родителя, включая импортированные модули 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()
...