Т.Л., др: вопрос в 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
. Если нет, то травление не удастся.
N.B .: здесь почти 5 утра, и теперь я слишком устал, чтобы продолжать копать эту очень интересную проблему! – zmo
Отличная работа! Это имеет смысл и объясняет все мои вопросы. Спасибо, я многому научился. Я попытаюсь проверить код многопроцессорности позже. –
Ага, это интересно. Кажется, это '__qualname__'. Я определил новый 'func2' и присвоил ему' func .__ qualname__'. Тогда traceback говорит '_pickle.PicklingError: Невозможно рассортировать: это не тот же объект, что и __main __. Func'. Нет места для вставки полной трассировки, так или иначе, в соответствии с трассировкой, мы можем взглянуть на 'multiprocessing/reduction.py, строка 50, в дампах: cls (buf, protocol) .dump (obj)'. –