带多线程的Windows上的Python Popen-can';t删除stdout/stderr日志
在Windows上使用Python2.7.4(注意:WinXP-下面的评论员建议这在Win7上正常工作),我有一个脚本可以创建几个 线程,每个线程通过Popen运行一个子进程 stdout/stderr已重定向到文件并调用wait()。每个人都有自己的爱好 拥有stdout/stderr文件。每次进程返回后,我有时会 删除文件(实际上将其移动到其他位置) 我发现在完成所有的测试之前,我无法删除stdout/stderr日志 wait()调用return。在此之前,我得到了“WindowsError:[Error 32] 进程无法访问该文件,因为另一个进程正在使用该文件 过程”。似乎Popen在某种程度上保留着stderr文件 只要至少有一个子进程处于打开状态,即使 这些文件不共享 测试代码复制如下 C:\test1.py带多线程的Windows上的Python Popen-can';t删除stdout/stderr日志,python,python-2.7,windows-xp,subprocess,python-multithreading,Python,Python 2.7,Windows Xp,Subprocess,Python Multithreading,在Windows上使用Python2.7.4(注意:WinXP-下面的评论员建议这在Win7上正常工作),我有一个脚本可以创建几个 线程,每个线程通过Popen运行一个子进程 stdout/stderr已重定向到文件并调用wait()。每个人都有自己的爱好 拥有stdout/stderr文件。每次进程返回后,我有时会 删除文件(实际上将其移动到其他位置) 我发现在完成所有的测试之前,我无法删除stdout/stderr日志 wait()调用return。在此之前,我得到了“WindowsErro
import subprocess
import threading
import os
def retryDelete(p, idx):
while True:
try:
os.unlink(p)
except Exception, e:
if "The process cannot access the file because it is being used by another process" not in e:
raise e
else:
print "Deleted logs", idx
return
class Test(threading.Thread):
def __init__(self, idx):
threading.Thread.__init__(self)
self.idx = idx
def run(self):
print "Creating %d" % self.idx
stdof = open("stdout%d.log" % self.idx, "w")
stdef = open("stderr%d.log" % self.idx, "w")
p = subprocess.Popen("c:\\Python27\\python.exe test2.py %d" % self.idx,
stdout=stdof, stderr = stdef)
print "Waiting %d" % self.idx
p.wait()
print "Starting deleting logs %d" % self.idx
stdof.close()
stdef.close()
retryDelete("stderr%d.log" % self.idx, self.idx)
print "Done %d" % self.idx
threads = [Test(i) for i in range(0, 10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
c:\test2.py:
import time
import sys
print "Sleeping",sys.argv[1]
time.sleep(int(sys.argv[1]))
print "Exiting",sys.argv[1]
如果运行此命令,您将看到每个retryDelete()都会在文件访问错误上旋转,直到所有子进程都完成为止
更新:即使未将stdof和stdef文件描述符传递给Popen构造函数,也会发生此问题。但是,如果删除Popen并用time.sleep(self.idx)替换wait(),则不会发生这种情况(即删除会立即发生)。由于Popen似乎对未传递给它的文件描述符有影响,我想知道这个问题是否与句柄继承有关
更新:close_fds=True会给出一个错误(在重定向stdout/stderr时Windows不支持),在wait()调用后删除带有del p的Popen对象对问题没有影响
更新:使用sysinternals process explorer查找具有文件句柄的进程。将测试减少到仅2个线程/子线程,并使第二个线程长时间保持打开状态。Handle搜索显示,stderr0.log中唯一有句柄的进程是父python进程,它有两个打开的句柄
更新:对于我当前的紧急用途,我找到了一个解决方法,即创建一个单独的脚本,将命令行和stderr/stdout日志文件作为参数,并运行重定向的子进程。然后,父级仅使用os.system()执行此帮助程序脚本。然后成功释放日志文件并将其删除。然而,我仍然对这个问题的答案感兴趣。对我来说,这感觉像是一个WinXP特有的bug,但仍然有可能我只是做错了什么。你可以尝试更新到Win7,我知道这是WinXP用户中常见的bug。这个问题很老,这个bug已经在Python 3.4+上修复。 为了记录在案,我们使用了一个黑客技巧来修复python 2.7或python 3.3上的问题- 这个函数是用纯python编写的(没有外部API),只在Windows上运行 =>在启动子流程之前,调用以下函数
def _hack_windows_subprocess():
"""HACK: python 2.7 file descriptors.
This magic hack fixes https://bugs.python.org/issue19575
by adding HANDLE_FLAG_INHERIT to all already opened file descriptors.
"""
# Extracted from https://github.com/secdev/scapy/issues/1136
import stat
from ctypes import windll, wintypes
from msvcrt import get_osfhandle
HANDLE_FLAG_INHERIT = 0x00000001
for fd in range(100):
try:
s = os.fstat(fd)
except:
break
if stat.S_ISREG(s.st_mode):
handle = wintypes.HANDLE(get_osfhandle(fd))
mask = wintypes.DWORD(HANDLE_FLAG_INHERIT)
flags = wintypes.DWORD(0)
windll.kernel32.SetHandleInformation(handle, mask, flags)
此函数将处理最后100个已打开的文件描述符,并将其设置为“无继承模式”,这将修复此错误。如果需要,可以增加100个数字。您是否应该将
stdof
和stdef
传递给Popen
?是-谢谢,简。但是,这与问题无关,问题在修复后仍然存在。我已经更新了这个例子。嗯,不过很有趣。也许这个问题与波本无关。我可能只是在做一些愚蠢的事情……不,如果我删除Popen并用time.sleep(self.idx)替换wait()调用,问题就会消失。可能此问题与句柄继承有关。我创建了一个问题: