使用sys.stdout.write和pool.map进行多处理 - 共享sys.stdout

时间:2014-12-16 21:27:01

标签: python python-2.7

这可能是我很遗憾的事情。

为什么我不能使用pool.map(sys.stdout.write, iterable)

我可以使用相同的可迭代来使用pool.map(len, iterable),但在使用sys.stdout.write时,我会遇到以下异常:

TypeError: expected string or Unicode object, NoneType found

这是追踪:

Traceback (most recent call last):
  File "/home/reut/python/print_mult.py", line 19, in <module>
    pool.map(sys.stdout.write, messages)
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 251, in map
    return self.map_async(func, iterable, chunksize).get()
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 558, in get
    raise self._value
TypeError: expected string or Unicode object, NoneType found

完整代码:

#!/usr/bin/env python

import multiprocessing
import sys

# pool of 10 workers
pool = multiprocessing.Pool(10)
messages = ["message #%d\n" % i for i in range(100)]
print messages
pool.map(sys.stdout.write, messages) # doesn't work - error
# print pool.map(len, messages) # works

编辑#1 - ThreadPool工作:

当我使用ThreadPool(来自multiprocessing.pool)时,它有效,所以我认为它与无法跨进程共享sys.stdout流有关。

编辑#2 - 手动流程也有效:

from multiprocessing import Process
import sys

# pool of 10 workers
processes = []
for i in range(10):
    processes.append(Process(target=sys.stdout.write, args=("I am process %d" % i, )))

for p in processes:
    p.start()

for p in processes:
    p.join()

所以现在我感到困惑,因为我知道常规流程和地图流程之间的差异就是它所要求的。我不确定这里的相关性如何。我唯一能想到的是,map在内部存储了target,并且无法像Process的手动构造函数那样与工作者共享它。

2 个答案:

答案 0 :(得分:3)

真正的错误是隐藏的。您只能传递一个可直接从模块名称空间引用的函数。但是,在某些情况下,有办法解决这个限制。 Unix有一个特殊的功能,可以分叉进程并复制其所有内存。这就是实例方法可以“传递”到子进程的方式 - 实际上没有传递任何东西。在Windows平台上,无法分叉进程,但必须生成进程。这意味着启动了一个新的解释器。为了让解释器运行给定的函数,它会发送要运行的函数的名称和它所在的模块。解释器在最终运行函数之前导入模块并查找函数。

对于属于池的进程,该进程已经启动,因此无法从分叉接收要运行的相应函数/方法的副本中受益。相反,它必须使用与生成新进程时相同的技术。这就是为什么你可以让你的第二次编辑工作,但不能让你的工作池。

解决问题的最简单方法是使print成为函数而不是语句。

from __future__ import print_function

import multiprocessing
import sys

if __name__ == '__main__':
    pool = multiprocessing.Pool(2)
    messages = ["message #%d\n" % i for i in range(5)]
    print(messages) # <- notice the brackets around the arguments to print
    pool.map(print, messages)

如果没有你可以定义一个能为你打印的功能,并将其用作地图的功能。

import multiprocessing 
import sys

def stdout_write(arg):
    sys.stdout.write(arg)

def stdout_print(arg):
    print arg

if __name__ == '__main__':
    pool = multiprocessing.Pool(2)
    messages = ["message #%d\n" % i for i in range(5)]
    print messages
    pool.map(stdout_print, messages)

答案 1 :(得分:2)

我确实不确定原因,但是pool.map()要求函数返回一个字符串。

对程序的这种简单更改可以正确运行。

#!/usr/bin/env python

import multiprocessing
import sys

def prn(s):
    sys.stdout.write(s)
    return ''

# pool of 10 workers
pool = multiprocessing.Pool(10)
messages = ["message #%d\n" % i for i in range(100)]
print messages
pool.map(prn, messages) # doesn't work - error
# print pool.map(len, messages) # works

我检查了文档,但我没有看到这个要求,所以我不知道为什么要强制执行。