Windowsでpython 2.7.4を使用する(注:WinXP - 以下のコメンターは、これがWin7で正しく動作することを示唆しています)、いくつかのスレッドを作成するスクリプトがあり、それぞれがPopenを介して子プロセスを実行し、stdout/stderrがファイルにリダイレクトされて呼び出します待つ()。各 Popen には独自の stdout/stderr ファイルがあります。各プロセスが戻った後、ファイルを削除する必要がある場合があります (実際には別の場所に移動します)。
すべての wait() 呼び出しが戻るまで、stdout/stderr ログを削除できないことがわかりました。その前に、「WindowsError: [エラー 32] 別のプロセスで使用されているため、プロセスはファイルにアクセスできません」というメッセージが表示されました。ファイルが共有されていなくても、少なくとも 1 つの子プロセスが開いている限り、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 が削除され、wait() が time.sleep(self.idx) に置き換えられた場合は発生しません (つまり、削除はすぐに行われます)。Popen は、渡されないファイル記述子に影響を与えているように見えるので、この問題がハンドルの継承に関連しているのではないかと思います。
UPDATE : close_fds=True でエラーが発生し (stdout/stderr をリダイレクトする場合、Windows ではサポートされていません)、wait() 呼び出しの後に del p で Popen オブジェクトを削除しても、問題に違いはありません。
更新: sysinternals プロセス エクスプローラーを使用して、ファイルへのハンドルを持つプロセスを探しました。テストを 2 つのスレッド/子に減らし、2 つ目のスレッドを長時間開いたままにしました。ハンドル検索により、stderr0.log へのハンドルを持つ唯一のプロセスは、2 つのハンドルが開かれている親の python プロセスであることがわかりました。
更新:現在の緊急の使用のために、回避策を見つけました。これは、コマンド ラインと stderr/stdout ログ ファイルをパラメーターとして受け取り、リダイレクトされた子プロセスを実行する別のスクリプトを作成することです。親は、このヘルパー スクリプトを os.system() で実行するだけです。その後、ログ ファイルは正常に解放され、削除されます。しかし、私はまだこの質問に対する答えに興味があります. 私には WinXP 固有のバグのように感じますが、何か間違ったことをしている可能性もあります。