Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
带多线程的Windows上的Python Popen-can';t删除stdout/stderr日志_Python_Python 2.7_Windows Xp_Subprocess_Python Multithreading - Fatal编程技术网

带多线程的Windows上的Python Popen-can';t删除stdout/stderr日志

带多线程的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

在Windows上使用Python2.7.4(注意:WinXP-下面的评论员建议这在Win7上正常工作),我有一个脚本可以创建几个 线程,每个线程通过Popen运行一个子进程 stdout/stderr已重定向到文件并调用wait()。每个人都有自己的爱好 拥有stdout/stderr文件。每次进程返回后,我有时会 删除文件(实际上将其移动到其他位置)

我发现在完成所有的测试之前,我无法删除stdout/stderr日志 wait()调用return。在此之前,我得到了“WindowsError:[Error 32] 进程无法访问该文件,因为另一个进程正在使用该文件 过程”。似乎Popen在某种程度上保留着stderr文件 只要至少有一个子进程处于打开状态,即使 这些文件不共享

测试代码复制如下

C:\test1.py

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()调用,问题就会消失。可能此问题与句柄继承有关。我创建了一个问题: