我正在使用我的python脚本创建大文件(超过1GB
,实际上有8个)。在我创建它们之后,我必须创建将使用这些文件的进程。
脚本如下:
# This is more complex function, but it basically does this:
def use_file():
subprocess.call(['C:\\use_file', 'C:\\foo.txt']);
f = open( 'C:\\foo.txt', 'wb')
for i in 10000:
f.write( one_MB_chunk)
f.flush()
os.fsync( f.fileno())
f.close()
time.sleep(5) # With this line added it just works fine
t = threading.Thread( target=use_file)
t.start()
但use_file
之类的应用foo.txt
是空的。有一些奇怪的事情发生了:
C:\use_file C:\foo.txt
(脚本完成后)我得到了正确的结果use_file()
,我会得到正确的结果C:\foo.txt
后,open()
在磁盘上可见,但在脚本结束前仍保持大小0B
time.sleep(5)
,它只是按预期开始工作(或者更确切地说是必需的)我已经找到了:
os.fsync()
但它似乎不起作用(use_file
的结果就好像C:\foo.txt
为空)buffering=(1<<20)
(打开文件时)似乎无法正常工作我对这种行为越来越好奇。
问题:
close()
操作到后台吗?这记录在哪里? sleep
后:是一个windows / python错误吗? 注意:(对于另一方出现问题的情况)应用程序use_data
使用:
handle = CreateFile("foo.txt", GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
size = GetFileSize(handle, NULL)
然后从size
处理foo.txt
个字节。
答案 0 :(得分:10)
f.close()
调用f.flush()
,将数据发送到操作系统。 不必须将数据写入磁盘,因为操作系统会缓冲它。正确的解决方法是,如果要强制操作系统将其写入磁盘,则需要os.fsync()
。
您是否考虑过直接将数据汇总到use_file
?
编辑:你说os.fsync()
'不起作用'。澄清,如果你这样做
f = open(...)
# write data to f
f.flush()
os.fsync(f.fileno())
f.close()
import pdb; pdb.set_trace()
然后查看磁盘上的文件,它是否有数据?
答案 1 :(得分:6)
编辑:使用特定于Python 3.x的信息进行更新
有一个超级旧的错误报告在https://bugs.python.org/issue4944讨论了一个可疑的类似问题。我做了一个小测试,显示了错误:https://gist.github.com/estyrke/c2f5d88156dcffadbf38
在上面的bug链接中获得用户eryksun的精彩解释之后,我现在明白为什么会发生这种情况,并且它本身并不是一个bug。在Windows上创建子进程时,默认情况下它会从父进程继承所有打开的文件句柄。因此,您所看到的实际上可能是共享冲突,因为您尝试在子进程中读取的文件是通过另一个子进程中的继承句柄打开的。导致这种情况发生的可能事件序列(使用上面Gist中的复制示例):
Thread 1 opens file 1 for writing
Thread 2 opens file 2 for writing
Thread 2 closes file 2
Thread 2 launches child 2
-> Inherits the file handle from file 1, still open with write access
Thread 1 closes file 1
Thread 1 launches child 1
-> Now it can't open file 1, because the handle is still open in child 2
Child 2 exits
-> Last handle to file 1 closed
Child 1 exits
当我编译简单的C子程序并在我的机器上运行脚本时,它在大多数情况下使用Python 2.7.8在至少一个线程中失败。使用Python 3.2和3.3,没有重定向的测试脚本不会失败,因为当不使用重定向时,close_fds
的{{1}}参数的默认值现在是subprocess.call
。在这些版本中,使用重定向的其他测试脚本仍然失败。在Python 3.4中,两个测试都成功,因为PEP 446默认情况下使所有文件句柄都不可继承。
从Python中的线程生成子进程意味着子进程继承所有打开文件句柄,甚至从生成子进程的其他线程继承。至少对我来说,这不是特别直观。
可能的解决方案:
True
传递给close_fds=True
以完全禁用继承(这是Python 3.x中的默认设置)。请注意,这可以防止重定向子进程的标准输入/输出/错误。subprocess.call
在Windows上打开带有os.open
标记的文件。
os.O_NOINHERIT
也使用此标记。请改用win32api。传递tempfile.mkstemp
参数的NULL指针也会阻止继承描述符:
lpSecurityAttributes