我尝试使用multiprocessing
包同时读取文件并在某些数据转换后覆盖(部分)文件。我理解它似乎有点抽象,但我有一个用于加速我自己的blocksync
fork的并发纠结。
您可以在下面找到我的代码片段:
#!/usr/bin/python2
import multiprocessing
import sys
import time
blocksize=1024
def do_open(f, mode):
f = open(f, mode)
f.seek(0, 2)
size = f.tell()
f.seek(0)
return f, size
def pipe_getblocks(f, pipe, side):
print "Child file object ID: "+str(id(f))
while True:
print "getblocks_seek_prev: "+str(f.tell())
block = f.read(blocksize)
if not block:
break
print "getblocks_seek_next: "+str(f.tell())
pipe.send(block)
def pipe_server(dev):
f, size = do_open(dev, 'r+')
parent,child = multiprocessing.Pipe(False)
reader = multiprocessing.Process(target=pipe_getblocks, args=(f,child,"R"))
reader.daemon = True
reader.start()
child.close()
i = 0
print "Parent file object ID:"+str(id(f))
while True:
try:
block = parent.recv()
except:
break
else:
print str(i)+":pseek: "+str(f.tell()/1024/1024)
f.seek(0,0) # This seek should not be see in the child subprocess...
i = i+1
pipe_server("/root/random.img")
基本上,父进程应该等待子进程填充,然后从中读取。请注意f.seek(0,0)
行:我将其放在此处以验证父母和孩子各自对文件中的搜索位置有自己的想法。换句话说,完全是两个不同的过程,我希望在父母身上做f.seek
对孩子没有影响。
然而,似乎这个假设是错误的,因为上述程序产生以下输出:
Child file object ID: 140374094691616
getblocks_seek_prev: 0
getblocks_seek_next: 1024
...
getblocks_seek_next: 15360
getblocks_seek_prev: 15360
getblocks_seek_next: 16384
getblocks_seek_prev: 16384
getblocks_seek_next: 17408 <-- past EOF!
getblocks_seek_prev: 17408 <-- past EOF!
getblocks_seek_next: 18432 <-- past EOF!
getblocks_seek_prev: 18432 <-- past EOF!
...
Parent file object ID:140374094691616
0:pseek: 0
1:pseek: 0
2:pseek: 0
3:pseek: 0
4:pseek: 0
5:pseek: 0
6:pseek: 0
7:pseek: 0
8:pseek: 0
9:pseek: 0
10:pseek: 0
...
正如您所看到的,子进程读取其EOF ,或者,认为,因为它实际上是从文件的开头读取的。简而言之,父母的f.seek(0,0)
似乎对子进程有影响,而没有认识到这一点。
我的假设是文件对象存储在共享内存中,因此两个进程都修改了相同的数据/对象。这个想法似乎得到了来自父进程和子进程的id(f)
的确认,它们报告了相同的数据。但是,我发现在使用multiprocessing
包时,没有引用声明文件对象保存在共享内存中。
所以,我的问题是:它是预期的行为,还是我遗漏了一些明显的东西?
答案 0 :(得分:3)
Python正在使用fork()
启动子进程,这会导致子进程从其父进程继承文件描述符。由于他们共享文件描述符,因此他们也共享相同的搜索偏移量。来自fork(2)
的联机帮助页:
孩子继承父母的一组打开文件的副本 描述。子中的每个文件描述符都是相同的 打开文件描述(参见open(2))作为相应的文件 父级中的描述符。这意味着这两个文件 描述符共享打开文件状态标志,文件偏移量和信号 - 驱动的I / O属性(参见F_SETOWN和 fcntl(2)中的F_SETSIG。
unix上的Python file
对象是文件描述符的非常薄的包装器(Python中的实现目前归结为fdno和一些关于路径的元数据; the seek()
method is just calling lseek(2)
),因此将对象克隆到子进程基本上只是发送文件描述符。
我能想到的最简单的解决方案是将路径传递给子进程并分别在每个进程中打开文件。你可以用os.dup
做一些棘手的事情,但是我不确定除了在你产生新进程时保存一些字节之外还有什么好处。
答案 1 :(得分:1)
我认为您希望为每个子进程单独打开。不要将文件对象作为参数传递,而是尝试将路径传递给文件,然后在多处理调用的函数内打开。