12. Juni 2007
Multi-Threading ist böse - Multi-Threading is evil
Bei Python gibt es da zwei spezielle Probleme:
- Im Gegensatz zu Java lassen sich Threads nicht im Notfall "abschießen".
- Wenn der Haupt-Thread endet, läuft das Programm trotzdem weiter -quasi scheintot - es sei denn, man arbeitet für alle Worker-Threads explizit mit setDaemon(true). Der Prozess wird dann beendet, sobald nur noch Daemon-Threads übrig sind.
Neben diesen Python-spezifischen Problemen gab es am Anfang die üblichen Schwierigkeiten mit
- Race Conditions bei konkurrienden Zugriffen, die zu falschen Ergebnissen führen,
- so dass schließlich an allen Ecken und Enden ein explizites Locking erforderlich war.
- Durch ungeschicktes Locking (unterschiedliche Reihenfolge) kam es dann gelegentlich zu Deadlocks, die schwierig zu finden waren (letzten Endes konnte ich dann aber programmatisch zur Laufzeit Warnungen bei möglichen Deadlocks ausgeben).
Viel gemeiner und bei weitem schwieriger zu entdecken waren aber die Probleme, die durch Race Conditions auftreten können, wenn ein Thread gerade eine Datei schreibt und ein anderer Thread in dieser Zeit einen Unterprozess startet, z.B. mit dem subprocess-Modul. Der Unterprozess erbt dann das Datei-Handles (unter Windows), woran natürlich niemand denkt. Dadurch ist die Datei für Windows quasi so lange zum Schreiben geöffnet, wie der Unterprozess läuft - z.B. kann der Prozess, der die Datei eigentlich geschrieben hat, die Datei anschließend nicht mit os.rename umbenennen. (Ja ja, ans zwischenzeitliche close habe ich gedacht).
Die Ursache ist die, dass alle Handles an Unterprozesse vererbt werden, wenn es nicht für das Handle explizit ausgeschaltet wird. Jedes Handle mit dem normalen open(...) wird also vererbt.
Als Workaround muss dann anstelle von outf = open (fname, "w") der Aufruf outf = os.fdopen (os.open (fname, os.O_WRITE + os.O_NOINHERIT + ....), "w") verwendet werden.