首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >创建Python多处理池与命名空间的交互

创建Python多处理池与命名空间的交互
EN

Stack Overflow用户
提问于 2016-03-09 08:18:04
回答 1查看 987关注 0票数 4

我们知道,必须在函数定义之后初始化multiprocessing.Pool以在其上运行。然而,我发现下面的代码对我来说是难以理解的。

代码语言:javascript
复制
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

输出(linux上的python2.7和python3.4 )是

代码语言:javascript
复制
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

代码语言:javascript
复制
def func2(i): print('second')

那么map-3就不会像许多帖子提到的那样找到func2这个名字了。this one。两个案子有什么区别?

据我所知,参数通过酸洗传递给从进程,但是pool如何将调用的函数传递给其他进程?或者子进程如何找到被调用的函数?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-03-12 03:46:40

tl;dr:在调用第一个funcmap-3上出现的问题,当人们期望第二个func会被调用时,是因为Pool.map()使用泡菜序列化func.__name__,这个泡菜即使被分配给func2引用也会解析为func,并且被发送到子进程,子进程查找func本地的子进程。

好的,我可以数四个不同的问题,下面列出,我认为您已经讲授了名称空间和分叉过程,以直接进入您的问题☺的乐趣

那么map-3是如何找到名称func2的呢? 因此func2 = func确实被执行,而def (I):print('second')不执行。为什么? 然后map-3就找不到许多帖子提到的名字func2,例如。这一个。两个案子有什么区别? 4据我所知,参数通过酸洗传递给从进程,但是池如何将调用的函数传递给其他进程?或者子进程如何找到被调用的函数?

因此,我增加了一些代码,以展示更多的内部结构:

代码语言:javascript
复制
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

它在我的系统上输出:

代码语言:javascript
复制
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的来源和调试器来了解发生了什么已经太晚了。

因此,如果我必须猜到1的答案,pickle模块就会发现func2解析为0x7f62d531bed8,而0x7f62d531bed8已经与标记func一起存在,因此它在子端将已知的“标签”func酸洗,然后解析到0x7f62d67f7cf8。即:

代码语言:javascript
复制
func2 → 0x7f62d531bed8 → func → [PICKLE] → globals()['func'] → 0x7f62d67f7cf8

为了验证我的理论,我修改了您的代码,将第二个func()重命名为func2(),如下所示:

代码语言:javascript
复制
------ 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

代码语言:javascript
复制
------ 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'

所以我相信我已经开始表达自己的观点了。同时,它还显示了在哪里读取代码来理解正在发生的事情,在子进程方面。

让更多的线索来回答2和3!

为了进一步了解,我在pool.py第114行中添加了一个print语句:

代码语言:javascript
复制
    job, i, func, args, kwds = task
    print("XXX", os.getpid(), job, i, func, args, kwds)

来展示到底发生了什么。我们可以看到,func被解析为0x7f2d0238fcf8,该地址与父函数中的地址相同:

代码语言:javascript
复制
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>

因此,为了回答4,我们需要进一步挖掘多处理源,甚至可能在泡菜源中。

但我想我对这个决议的感觉可能是对的,…然后剩下的唯一的问题是,为什么它会将标签解析为地址,然后再返回标签,然后再将其推送给子进程!

编辑:我想我知道为什么!当我上床睡觉的时候,我突然想到了一个原因,于是我回到我的键盘上:

在对函数进行酸洗时,泡菜接受包含该函数的参数,并从函数的对象本身获取其名称:

因此,即使您确实创建了一个新的函数对象,但是您在内存中确实获得了一个不同的地址:

代码语言:javascript
复制
>>> print(func)
<function func at 0x7fc6174e3ed8>

pickles并不在意,因为如果这个函数还没有被孩子访问,它将永远无法访问。所以泡菜只能解决func.__name__

代码语言:javascript
复制
>>> print("func.__name__:", func.__name__)
func.__name__: func
>>> print("func2.__name__:", func2.__name__)
func2.__name__: func

然后,即使您在父线程上更改了函数的主体,并且对该函数进行了新的引用,但真正被选中的是函数的内部名称,它是在分配lambda或定义函数时给出的。

这解释了为什么在func阶段将func2交给pool1时会得到旧的map-3函数。

因此,作为一个结论,对于1 map-3没有找到名称func2,它会在func2引用的函数中找到名称func。因此,这也回答了2&3,因为找到的func正在执行原始的func函数。其机制是,func.__name__被用来对两个进程之间的函数名进行筛选和解析,应答4。

最后一次更新,来自您:

pickle._Pickler.save_global中,它使用

代码语言:javascript
复制
if name is None: name = getattr(obj, '__qualname__', None)

然后再一次

代码语言:javascript
复制
if name is None: name = obj.__name__. 

因此,如果obj没有__qualname__,那么将使用__name__

但是,它将检查传递的对象是否与子进程中的对象相同:

代码语言:javascript
复制
if obj2 is not obj: raise PicklingError(...) 

在哪里obj2, parent = _getattribute(module, name)

是的,但是请记住,传递的对象只是函数的(内部)名称,而不是函数本身。子进程没有方法来判断其func()与内存中父进程的func()是否相同。

从@SyrtisMajor编辑:

好的,让我们修改上面的第一段代码:

代码语言:javascript
复制
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

产出如下:

代码语言:javascript
复制
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相同。如果没有,那么腌制就会失败。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35886272

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档