[英]Python multiprocessing and file seeks
我正在尝试使用multiprocessing
程序包来同时读取文件并在进行一些数据转换后将其覆盖(部分)。 我知道这似乎有点抽象,但是我可以利用这种并发方式来blocksync
我自己的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)
的f.seek(0,0)
对子进程有影响,而没有意识到这一点。
我的假设是文件对象存储在共享内存中,因此两个进程都在修改相同的数据/对象。 这个想法似乎由来自父进程和子进程的id(f)
证实,它们报告相同的数据。 但是,我找不到引用说明使用multiprocessing
程序包时文件对象保留在共享内存中。
所以,我的问题是:这是预期的行为,还是我缺少明显的东西?
Python正在使用fork()
启动子进程,这将导致子进程从其父级继承文件描述符。 由于它们共享文件描述符,因此它们也共享相同的寻道偏移量。 从fork(2)
的联机帮助页中:
子级继承父级打开文件描述符集的副本。 子级中的每个文件描述符都引用与父级中相应的文件描述符相同的打开文件描述(请参阅open(2))。 这意味着这两个文件描述符共享打开的文件状态标志,文件偏移和信号驱动的I / O属性(请参见fcntl(2)中对F_SETOWN和F_SETSIG的描述)。
Unix上的Python file
对象是文件描述符的非常薄的包装器(Python中的实现当前可归结为fdno和有关路径的一些元数据; seek()
方法仅调用lseek(2)
),因此将对象克隆到子进程基本上只是在发送文件描述符。
我能想到的最简单的解决方案是将路径传递给子进程,并分别在每个进程中打开文件。 您也许可以使用os.dup
做一些棘手的事情,但是我不确定在产生新进程时,除了节省一些字节之外,还有什么好处。
我认为您想为每个子进程单独打开一个窗口。 不要将文件对象作为参数传递,而是尝试将路径传递到文件,然后在多处理调用的函数内部打开。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.