Mit Python 2.7.4 unter Windows (Hinweis: WinXP - ein Kommentar unten schlägt vor, dies funktioniert ordnungsgemäß auf Win7), habe ich ein Skript, das mehrere Threads erstellt Jede davon führt einen untergeordneten Prozess über Popen mit der stdout/stderr umgeleitet zu Dateien und Aufrufe wait(). Jeder Popen hat seine eigenen stdout/stderr Dateien. Nach jedem Prozess gibt es manchmal , um die Dateien zu löschen (tatsächlich verschieben sie sie woanders).Python Popen unter Windows mit Multithreading - kann nicht stdout/stderr logs
Ich finde, dass ich die stdout/stderr Protokolle erst löschen kann, nachdem alle wait() -Aufrufe zurückgeben. Vorher bekomme ich "WindowsError: [Fehler 32] Der Prozess kann nicht auf die Datei zugreifen, weil sie von einem anderen Prozess verwendet wird". Es scheint, dass Popen die stderr-Dateien so lange festhält, wie mindestens ein untergeordneter Prozess geöffnet ist, obwohl die Dateien nicht gemeinsam genutzt werden.
Testcode zur Reproduktion unten.
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]
Wenn Sie dies ausführen, werden Sie sehen, dass jede retryDelete() dreht sich auf der Zugriffsfehlerdatei bis alle Kindprozesse beendet sind.
UPDATE: Das Problem tritt auf, selbst wenn die stdof und stdef Dateideskriptoren nicht in den Popen-Konstruktor übergeben werden. Es geschieht jedoch nicht (d. H. Die Löschvorgänge werden sofort ausgeführt), wenn Popen entfernt und wait() durch time.sleep (self.idx) ersetzt wird. Da der Popen anscheinend Auswirkungen auf Dateideskriptoren hat, die nicht an ihn übergeben werden, frage ich mich, ob dieses Problem mit der Vererbung von Handles zu tun hat.
UPDATE: close_fds = True gibt einen Fehler (unter Windows nicht unterstützt, wenn stdout/stderr Umleitung) und mit del p nach dem wait() Aufruf des Popen Objekt zu löschen macht keinen Unterschied zu dem Thema.
UPDATE: Sysinternals Process Explorer verwendet, um nach Prozessen mit Handles für die Datei zu suchen. Reduzierte den Test auf nur 2 Threads/Kinder und ließ die zweite lange offen bleiben. Die Handle-Suche zeigte, dass der einzige Prozess mit Handles zu stderr0.log der Parent-Python-Prozess war, für den zwei Handles geöffnet waren.
UPDATE: Für meinen aktuellen, dringenden Gebrauch, ich habe eine Abhilfe gefunden, die ein separates Skript zu erstellen, die die Befehlszeile nimmt und stderr/stdout-Log-Dateien als Parameter und führen das Kind Prozess umgeleitet. Das übergeordnete Element führt dieses Hilfsskript nur mit os.system() aus. Die Protokolldateien werden dann erfolgreich freigegeben und gelöscht. Ich bin jedoch immer noch an der Antwort auf diese Frage interessiert. Es fühlt sich für mich wie ein WinXP-spezifischer Fehler an, aber es ist immer noch möglich, dass ich nur etwas falsch mache.
Sollst du "stdof" und "stdef" an "Popen" weitergeben? –
Ja - Danke, Janne. Das hängt jedoch nicht mit dem Problem zusammen, das nach der Behebung weiterhin besteht. Ich habe das Beispiel aktualisiert. – Tom
Hmmm - interessant, obwohl. Vielleicht hängt das Thema nicht mit Popen zusammen. Ich mache wahrscheinlich nur etwas dummes ... – Tom