将sys.stdout作为参数传递给进程

时间:2015-12-16 14:40:16

标签: python multiprocessing stdout sys

我传递“sys.stdout”作为进程的参数,然后进程在执行其内容时写入“sys.stdout”。

import multiprocessing
import sys
def worker_with(stream):
    stream.write('In the process\n')

if __name__ == '__main__':
    sys.stdout.write('In the main\n')
    lock = multiprocessing.Lock()

    w = multiprocessing.Process(target=worker_with, args=(sys.stdout,))

    w.start()
    w.join()

上面的代码不起作用,它返回以下错误:“ValueError:关闭文件上的操作”。

我尝试运行相同的代码但是直接调用该函数而不是生成一个进程并且它可以工作,它打印到控制台。 我也尝试运行相同的代码,但直接在函数内部调用sys.stdout,将其作为一个进程生成它并且它可以工作。 问题似乎是将sys.stout作为进程的参数传递。

有人知道为什么吗?

注意:此代码的灵感来自教程PYMOTW - 进程之间的通信。

编辑:我在Windows7上运行Python 2.7.10,32位。

1 个答案:

答案 0 :(得分:5)

当您将参数传递给Process时,它们会在父项中被腌制,传输给孩子,然后在那里进行打开。不幸的是,对于文件对象来说,看起来像是pickle无意中行为错误的往返行程;使用协议0,它会出错,但是使用协议2(最高的Python 2协议,以及用于multiprocessing的协议),它会静默生成一个垃圾文件对象:

>>> import pickle, sys
>>> pickle.loads(pickle.dumps(sys.stdout, pickle.HIGHEST_PROTOCOL))
<closed file '<uninitialized file>', mode '<uninitialized file>' at 0xDEADBEEF>

命名文件也会出现同样的问题;它并非标准手柄的独特之处。基本上,pickle不能往返文件对象;即使它声称成功,结果也是垃圾。

一般来说,multiprocessing并不是真的希望处理这样的场景;通常,Process es是工作任务,I / O是通过主进程执行的(因为如果它们都独立写入相同的文件句柄,则会出现交错写入问题)。

至少在Python 3.5中,他们修复了这个问题,因此错误很明显(openTextIOWrapperBuffered*返回的类似文件的对象在pickle时会出错任何协议)。

您在Windows上可以做的最好的事情是将已知的文件描述符作为参数发送:

sys.stdout.flush()  # Precaution to minimize output interleaving
w = multiprocessing.Process(target=worker_with, args=(sys.stdout.fileno(),))

然后使用os.fdopen在另一侧重新打开它。 fd不属于标准句柄(012),因为Windows使用&#34; spawn&#34;在{{1} {{1 (Windows通过导入Process模块模拟fd,将import设置为其他内容。当然,如果它是一个命名文件,而不是标准句柄,你可以只传递名称并重新打开它。例如,为了实现这一目标,您需要更改:

__main__

为:

__name__ != "__main__"

注意:如上所述,如果fork用于其中一个标准句柄,__main__将在__name__语句退出时关闭基础文件描述符,这可能不是你想要的。如果您需要文件描述符在def worker_with(stream): stream.write('In the process\n') 块结束时继续存在,则在传递文件描述符时,您可能希望在调用import os def worker_with(toopen): opener = open if isinstance(toopen, basestring) else os.fdopen with opener(toopen, 'a') as stream: stream.write('In the process\n') 之前使用os.dup复制句柄,因此这两个句柄是彼此独立的。

其他解决方案包括通过fd将结果写回主进程(因此主进程负责将数据传递到os.fdopen,可能启动一个线程以异步方式执行此工作) ,或使用更高级别的构造(例如with),使用with语句而不是显式文件I / O返回数据。

如果您真的非常渴望为所有文件描述符做一般工作(并且不关心可移植性),而不仅仅是os.fdopen multiprocessing.Pipe上创建的标准句柄和描述符。 1}},您可以使用the undocumented Windows utility function multiprocessing.forking.duplicate来明确地将文件描述符从一个进程复制到另一个进程;这将是非常hacky(你需要查看sys.stdout的其余Windows定义,看看它将如何使用),但它至少允许传递任意文件描述符,而不是只是静态打开的。