2016-03-09 3 views
3

Мы знаем, что multiprocessing.Pool должен быть инициализирован после определения функций, выполняемых на них. Тем не менее я нашел следующий код непостижим для меняВзаимодействие пула многопроцессорного пула с пространством имен при создании

import os 
from multiprocessing import Pool 

def func(i): print('first') 

pool1 = Pool(2) 
pool1.map(func, range(2))   #map-1 

def func(i): print('second') 
func2 = func 

print('------') 
pool1.map(func, range(2))  #map-2 
pool1.map(func2, range(2))  #map-3 

pool2 = Pool(2) 
print('------') 
pool2.map(func, range(2))  #map-4 
pool2.map(func2, range(2))  #map-5 

Выхода (python2.7 и python3.4 на Linux) является

first   #map-1 
first 
------ 
first   #map-2 
first 
first   #map-3 
first 
------ 
second  #map-4 
second 
second  #map-5 
second 

map-2 печати 'first' так же, как мы ожидали. Но как map-3 найти имя func2? Я имею в виду, что pool1 инициализирован до первого появления func2. Таким образом, func2 = func действительно выполнен, а def func(i): print('second') - нет. Зачем?

И если я определяю func2 непосредственно

def func2(i): print('second') 

Тогда map-3 не найдет имя func2 как упомянуто многими постами, например. this one. В чем разница между двумя случаями?

Как я понимаю, аргументы передаются подчиненным процессам путем травления, но как pool передает вызываемую функцию другим процессам? Или как подпроцессы находят вызываемую функцию?

ответ

1

Т.Л., др: вопрос в map-3, где первый func вызывается, когда можно было бы ожидать, что второй func быть связано с тем, что Pool.map() сериализует func.__name__ с рассол, который решает в func даже если он является присвоенный ссылке func2, и отправляется дочернему процессу, который локально ищет дочерний процесс func.



нормально, так что я могу рассчитывать четыре различные вопросы, перечисленные ниже, и я считаю, что вы уже читал лекции о пространствах имен и порождения процессов, чтобы получить прямо в удовольствие от вашего вопроса ☺

① But how does map-3 find the name func2?

② So func2 = func is indeed executed, while def func(i): print('second') is not. Why?

③ Then map-3 won't find name func2 as mentioned by many posts, eg. this one. What's the difference between two cases?

④ As I understand the arguments are passed to the slave processes by pickling, but how does pool pass the called function to other processes? Or how do sub-processes find the called function?

Так я добавил немного больше кода, чтобы показать больше внутренностей:

import os 
from multiprocessing import Pool 

print(os.getpid(), 'parent') 

def func(i): 
    print(os.getpid(), 'first', end=" | ") 
    if 'func' in globals(): 
     print(globals()['func'], end=" | ") 
    else: 
     print("no func in globals", end=" | ") 
    if 'func2' in globals(): 
     print(globals()['func2']) 
    else: 
     print("no func2 in globals") 

print('------ map-1') 
pool1 = Pool(2) 
pool1.map(func, range(2))   #map-1 

def func(i): 
    print(os.getpid(), 'second', end=" | ") 
    if 'func' in globals(): 
     print(globals()['func'], end=" | ") 
    else: 
     print("no func in globals", end=" | ") 
    if 'func2' in globals(): 
     print(globals()['func2']) 
    else: 
     print("no func2 in globals") 
func2 = func 

print('------ map-2') 
pool1.map(func, range(2))  #map-2 
print('------ map-3') 
pool1.map(func2, range(2))  #map-3 

pool2 = Pool(2) 
print('------ map-4') 
pool2.map(func, range(2))  #map-4 
print('------ map-5') 
pool2.map(func2, range(2))  #map-5 

который OUTP UTS на моей системе:

21512 parent 
------ map-1 
21513 first | <function func at 0x7f62d67f7cf8> | no func2 in globals 
21514 first | <function func at 0x7f62d67f7cf8> | no func2 in globals 
------ map-2 
21513 first | <function func at 0x7f62d67f7cf8> | no func2 in globals 
21514 first | <function func at 0x7f62d67f7cf8> | no func2 in globals 
------ map-3 
21513 first | <function func at 0x7f62d67f7cf8> | no func2 in globals 
21514 first | <function func at 0x7f62d67f7cf8> | no func2 in globals 
------ map-4 
21518 second | <function func at 0x7f62d531bed8> | <function func at 0x7f62d531bed8> 
21519 second | <function func at 0x7f62d531bed8> | <function func at 0x7f62d531bed8> 
------ map-5 
21518 second | <function func at 0x7f62d531bed8> | <function func at 0x7f62d531bed8> 
21519 second | <function func at 0x7f62d531bed8> | <function func at 0x7f62d531bed8> 

так, мы можем видеть, что для pool1 никогда не является func2 добавляется к пространству имен. Таким образом, есть определенно что-то подозрительное, и мне уже слишком поздно хорошо смотреть на источник multiprocessing и на отладчик, чтобы понять, что происходит.

Так что, если я должен был угадать ответ на ①, то pickle модуль выяснить каким-то образом, что func2 решает 0x7f62d531bed8, который уже существует с тегом func, таким образом, это соленья уже известно «метка» func на стороне детей , разрешая там до 0x7f62d67f7cf8. то есть:

func2 → 0x7f62d531bed8 → func → [PICKLE] → globals()['func'] → 0x7f62d67f7cf8 

Чтобы проверить свою теорию, я изменил код немного, путем переименования второй func() в func2() и вот что у меня получилось:

------ map-3 
Process PoolWorker-1: 
Process PoolWorker-2: 
Traceback (most recent call last): 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap 
    self.run() 
    self.run() 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run 
    self._target(*self._args, **self._kwargs) 
    self._target(*self._args, **self._kwargs) 
    File "/usr/lib/python2.7/multiprocessing/pool.py", line 102, in worker 
    File "/usr/lib/python2.7/multiprocessing/pool.py", line 102, in worker 
    task = get() 
    task = get() 
    File "/usr/lib/python2.7/multiprocessing/queues.py", line 376, in get 
    File "/usr/lib/python2.7/multiprocessing/queues.py", line 376, in get 
    return recv() 
    return recv() 
AttributeError: 'module' object has no attribute 'func2' 
AttributeError: 'module' object has no attribute 'func2' 

, а затем меняется, а func = func2 в func2 = func

------ map-2 
Process PoolWorker-1: 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap 
Process PoolWorker-2: 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap 
    self.run() 
    self.run() 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run 
    self._target(*self._args, **self._kwargs) 
    self._target(*self._args, **self._kwargs) 
    File "/usr/lib/python2.7/multiprocessing/pool.py", line 102, in worker 
    File "/usr/lib/python2.7/multiprocessing/pool.py", line 102, in worker 
    task = get() 
    task = get() 
    File "/usr/lib/python2.7/multiprocessing/queues.py", line 376, in get 
    File "/usr/lib/python2.7/multiprocessing/queues.py", line 376, in get 
    return recv() 
    return recv() 
AttributeError: 'module' object has no attribute 'func2' 
AttributeError: 'module' object has no attribute 'func2' 

Итак, я считаю, что я начинаю говорить. А также показывает, где читать код, чтобы понять, что происходит, на стороне дочерних процессов.

Так что больше подсказок, чтобы ответить ② и ③!

Чтобы получить дальше, я добавил оператор печати в pool.py линии 114:

job, i, func, args, kwds = task 
    print("XXX", os.getpid(), job, i, func, args, kwds) 

, чтобы показать, что происходит. И мы видим, что func разрешен к 0x7f2d0238fcf8, что тот же адрес, в родительской функции:

23432 parent 
------ map-1 
('XXX', 23433, 0, 0, <function mapstar at 0x7f2d02363230>, ((<function func at 0x7f2d0238fcf8>, (0,)),), {}) 
23433 first | <function func at 0x7f2d0238fcf8> | no func2 in globals 
('XXX', 23434, 0, 1, <function mapstar at 0x7f2d02363230>, ((<function func at 0x7f2d0238fcf8>, (1,)),), {}) 
23434 first | <function func at 0x7f2d0238fcf8> | no func2 in globals 
------ map-2 
('XXX', 23433, 1, 0, <function mapstar at 0x7f2d02363230>, ((<function func at 0x7f2d0238fcf8>, (0,)),), {}) 
23433 first | <function func at 0x7f2d0238fcf8> | no func2 in globals 
('XXX', 23434, 1, 1, <function mapstar at 0x7f2d02363230>, ((<function func at 0x7f2d0238fcf8>, (1,)),), {}) 
23434 first | <function func at 0x7f2d0238fcf8> | no func2 in globals 
------ map-3 
('XXX', 23433, 2, 0, <function mapstar at 0x7f2d02363230>, ((<function func at 0x7f2d0238fcf8>, (0,)),), {}) 
23433 first | <function func at 0x7f2d0238fcf8> | no func2 in globals 
('XXX', 23434, 2, 1, <function mapstar at 0x7f2d02363230>, ((<function func at 0x7f2d0238fcf8>, (1,)),), {}) 
23434 first | <function func at 0x7f2d0238fcf8> | no func2 in globals 
------ map-4 
('XXX', 23438, 3, 0, <function mapstar at 0x7f2d02363230>, ((<function func at 0x1092e60>, (0,)),), {}) 
23438 second | <function func at 0x1092e60> | <function func at 0x1092e60> 
('XXX', 23439, 3, 1, <function mapstar at 0x7f2d02363230>, ((<function func at 0x1092e60>, (1,)),), {}) 
23439 second | <function func at 0x1092e60> | <function func at 0x1092e60> 
------ map-5 
('XXX', 23438, 4, 0, <function mapstar at 0x7f2d02363230>, ((<function func at 0x1092e60>, (0,)),), {}) 
('XXX', 23439, 4, 1, <function mapstar at 0x7f2d02363230>, ((<function func at 0x1092e60>, (1,)),), {}) 
23438 second | <function func at 0x1092e60> | <function func at 0x1092e60> 
23439 second | <function func at 0x1092e60> | <function func at 0x1092e60> 

Итак, чтобы ответить ④, мы должны были бы копать дальше в источниках многопроцессорных, и даже может быть в пределах источники рассола.

Но я думаю, мое мнение по поводу резолюции, вероятно, правы ... И тогда только остается вопрос, почему делает это решает этикетки на адреса и обратно к меткам снова, прежде чем настаивать, что дети процессы!


Редактировать: Я думаю, что знаю почему! Когда я ложился спать, причина всплыла у меня в голове, поэтому я просто вернулся к своей клавиатуре:

При расчете функции рассолы принимают аргумент, содержащий эту функцию, и получают свое имя от самого объекта функции:

так что даже если вы создаете новый функциональный объект, на который вы не получите другой адрес в памяти:

>>> print(func) 
<function func at 0x7fc6174e3ed8> 

соленье не заботится, потому что если эта функция уже не доступная ребенка, он никогда не станет доступным. Так рассол только решает func.__name__:

>>> print("func.__name__:", func.__name__) 
func.__name__: func 
>>> print("func2.__name__:", func2.__name__) 
func2.__name__: func 

, а затем, даже если вы изменили тело функции на родительской нити, и вы сделали новую ссылка на эту функцию, что на самом деле получает маринованное это внутреннее имя функции, который задается при назначении лямбда или определении функции.

Это объясняет, почему вы получаете старую func функцию, когда вы даете func2 к pool1 на map-3 стадии.

Так как заключение для ① map-3 не находит имя func2, это найти имя func внутри функции, указанной на func2.Итак, это также отвечает ② & ③, так как найденное func выполняет оригинальную функцию func. Механизм - это тот факт, что func.__name__ используется для рассортировки и разрешения имени функции между двумя процессами, отвечая ④.


Последнее обновление от вас:

В pickle._Pickler.save_global, он получает имя с помощью

if name is None: name = getattr(obj, '__qualname__', None) 

затем снова

if name is None: name = obj.__name__. 

Так что, если OBJ имеет нет __qualname__ тогда __name__ будет использован.

However it will check if the object passed is same with the one in subprocess:

if obj2 is not obj: raise PicklingError(...) 

obj2, parent = _getattribute(module, name) где.

yup, но помните, что переданный объект является просто внутренним именем функции, а не самой функцией. У детского процесса есть no способ узнать, соответствует ли его func() родительскому func() в памяти.


Edit из @SyrtisMajor:

ОК, давайте изменим первый код, указанный выше:

import os 
from multiprocessing import Pool 

print(os.getpid(), 'parent') 

def func(i): 
    print(os.getpid(), 'first', end=" | ") 
    if 'func' in globals(): 
     print(globals()['func'], end=" | ") 
    else: 
     print("no func in globals", end=" | ") 
    if 'func2' in globals(): 
     print(globals()['func2']) 
    else: 
     print("no func2 in globals") 

print('------ map-1') 
pool1 = Pool(2) 
pool1.map(func, range(2))   #map-1 

def func2(i): 
    print(os.getpid(), 'second', end=" | ") 
    if 'func' in globals(): 
     print(globals()['func'], end=" | ") 
    else: 
     print("no func in globals", end=" | ") 
    if 'func2' in globals(): 
     print(globals()['func2']) 
    else: 
     print("no func2 in globals") 

func2.__qualname__ = func.__qualname__ 

func = func2 

print('------ map-2') 
pool1.map(func, range(2))  #map-2 
print('------ map-3') 
pool1.map(func2, range(2))  #map-3 

pool2 = Pool(2) 
print('------ map-4') 
pool2.map(func, range(2))  #map-4 
print('------ map-5') 
pool2.map(func2, range(2))  #map-5 

выходов следующим образом:

38130 parent 
------ map-1 
38131 first | <function func at 0x101856f28> | no func2 in globals 
38132 first | <function func at 0x101856f28> | no func2 in globals 
------ map-2 
38131 first | <function func at 0x101856f28> | no func2 in globals 
38132 first | <function func at 0x101856f28> | no func2 in globals 
------ map-3 
38131 first | <function func at 0x101856f28> | no func2 in globals 
38132 first | <function func at 0x101856f28> | no func2 in globals 
------ map-4 
38133 second | <function func at 0x10339b510> | <function func at 0x10339b510> 
38134 second | <function func at 0x10339b510> | <function func at 0x10339b510> 
------ map-5 
38133 second | <function func at 0x10339b510> | <function func at 0x10339b510> 
38134 second | <function func at 0x10339b510> | <function func at 0x10339b510> 

Это точно так же, как наш первый выпуск. И обратите внимание, что func = func2 после определения func2 является ключевым, поскольку рассол будет проверять, соответствует ли func2 (с именем func) __main__.func. Если нет, то травление не удастся.

+1

N.B .: здесь почти 5 утра, и теперь я слишком устал, чтобы продолжать копать эту очень интересную проблему! – zmo

+0

Отличная работа! Это имеет смысл и объясняет все мои вопросы. Спасибо, я многому научился. Я попытаюсь проверить код многопроцессорности позже. –

+0

Ага, это интересно. Кажется, это '__qualname__'. Я определил новый 'func2' и присвоил ему' func .__ qualname__'. Тогда traceback говорит '_pickle.PicklingError: Невозможно рассортировать : это не тот же объект, что и __main __. Func'. Нет места для вставки полной трассировки, так или иначе, в соответствии с трассировкой, мы можем взглянуть на 'multiprocessing/reduction.py, строка 50, в дампах: cls (buf, protocol) .dump (obj)'. –

Смежные вопросы