6

Windowsでpython 2.7.4を使用しています(注:WinXP - 以下のコメント欄はWin7で正しく動作することを示唆しています)、私はいくつかの スレッドそれぞれがPopenを介して子プロセスを実行し、 stdout/stderrがファイルにリダイレクトされ、wait()が呼び出されます。各Popenには のstdout/stderrファイルがあります。各プロセスが復帰すると、ファイルを削除するのに実際には があります(実際には別の場所に移動します)。マルチスレッドのPython Popen - stdout/stderrログを削除できません

私は、すべての wait()呼び出しが返されるまでstdout/stderrログを削除できないことを発見しました。その前に私は "WindowsError:[エラー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()は、ファイルアクセスエラーにスピンしていることがわかりますすべての子プロセスが終了するまで

UPDATE:この問題は、stdofおよびstdefファイル記述子がPopenコンストラクタに渡されなくても発生します。しかし、Popenが削除され、wait()がtime.sleep(self.idx)に置き換えられた場合、それは起こりません(つまり、直ちに削除が行われます)。 Popenはそれに渡されないファイル記述子に影響を与えているように見えるので、この問題が継承を処理することに関連しているのだろうかと思います。

UPDATE:close_fds = Trueの場合(標準出力/標準エラー出力をリダイレクトしないときに、Windowsでサポートされています)エラーを与え、待機()呼び出しの後にデルのpでpopenのオブジェクトを削除する問題に違いはありません。

UPDATE:ファイルへのハンドルを持つプロセスを探すためにsysinternalsプロセスエクスプローラを使用しました。テストをわずか2スレッド/子供に減らし、2番目のテストを長時間開いたままにしました。ハンドル検索では、stderr0.logへのハンドルを持つ唯一のプロセスが、親Pythonプロセスであり、ハンドルが2つ開いていることがわかりました。

UPDATE:私の現在、緊急の使用のために、私はパラメータとしてコマンドラインとstderr/stdoutをログファイルを取り、リダイレクトされた子プロセスを実行する別のスクリプトを作成することです回避策を、見つけました。親はos.system()でこのヘルパースクリプトを実行するだけです。その後、ログファイルは正常に解放され、削除されます。しかし、私はまだこの質問の答えに興味があります。 WinXP固有のバグのような感じですが、まだ間違っています。

+0

'' Popen'に 'stdof'と' stdef'を渡すはずですか? –

+0

はい - ありがとう、Janne。しかし、それはこの問題とは無関係であり、修正後も維持されます。私は例を更新しました。 – Tom

+0

Hmmm - 面白いですが。多分問題はPopenに関連していないでしょう。私はおそらくちょうど何かばかげている... – Tom

答えて

0

Win7にアップデートしようとすることができます。これはWinXPユーザーの一般的なバグです。

0

この問題は古く、このバグはPython 3.4+で修正されています。 ここでは、python 2.7またはpython 3の問題を解決するために使用してきたハックトリックを記録します。3 -

This function is made in pure python (no external APIs), and only works on 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を増やすことができます。

関連する問題