我传递"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位.
当您将参数传递给a时Process
,它们会在父级中被腌制,传输给子级,并在那里进行切换.不幸的是,pickle
对于文件对象,它看起来像是默默地行为不端; 使用协议0,它会出错,但是使用协议2(最高的Python 2协议,以及用于协议的协议multiprocessing
),它会静默生成一个垃圾文件对象:
>>> import pickle, sys >>> pickle.loads(pickle.dumps(sys.stdout, pickle.HIGHEST_PROTOCOL))', mode ' ' at 0xDEADBEEF>
命名文件也会出现同样的问题; 它并非标准手柄的独特之处.基本上,pickle
不能往返文件对象; 即使它声称成功,结果也是垃圾.
通常,multiprocessing
实际上并不期望处理这样的场景; 通常,Process
es是工作任务,I/O是通过主进程执行的(因为如果它们都独立写入相同的文件句柄,则会出现交错写入问题).
至少在Python 3.5中,他们修复了这个错误,因此错误很明显(返回的文件类对象open
,TextIOWrapper
并且Buffered*
在使用任何协议进行pickle时都会出错).
您在Windows上可以做的最好的事情是将已知的文件描述符作为参数发送:
sys.stdout.flush() # Precaution to minimize output interleaving w = multiprocessing.Process(target=worker_with, args=(sys.stdout.fileno(),))
然后在另一边重新打开它os.fdopen
.对于fd
不是标准手柄部分(0
,1
和2
),因为Windows用制造新的"重生"的方法Process
ES,你需要确保任何这样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')
注意:如上所述,如果fd
是标准句柄之一,os.fdopen
则在with
语句退出时将关闭基础文件描述符,这可能不是您想要的.如果您需要文件描述符在with
块关闭后继续存在,则在传递文件描述符时,您可能希望os.dup
在调用之前使用复制句柄os.fdopen
,因此这两个句柄彼此独立.
其他解决方案包括将结果写回主进程multiprocessing.Pipe
(因此主进程负责传递数据sys.stdout
,可能启动线程以异步执行此工作),或使用更高级别的构造(例如multiprocessing.Pool().*map*
)使用返回数据return
语句而不是显式文件I/O.
如果你真的绝望,使一般这项工作的所有的文件描述符(和不关心的可移植性),而不仅仅是创建标准的句柄和描述符import
的__main__
,你可以使用无证的Windows效用函数multiprocessing.forking.duplicate
是用来明确将文件描述符从一个进程复制到另一个进程; 它将是非常hacky(你需要查看其余的Windows定义,multiprocessing.forking.Popen
看看它将如何使用),但它至少允许传递任意文件描述符,而不仅仅是静态打开的文件描述符.