Teil von SELFHTML aktuell Teil von Artikel Teil von Programmiertechnik Teil von Sperren von Dateien

Sperren von Dateien: Threads - Stolperfallen der modernen Performanceoptimierung

nach unten Allgemeines
nach unten PHP/Perl/Python und Webserver mit Threads
nach unten Java mit unterschiedlichen Threads, Servlets

Allgemeines

Dateisperren werden durch ein Konzept verkompliziert: Threads. Genauso, wie die Resourcen eines Computers unter Prozessen aufgeteilt werden können, die parallel ablaufen, können die Resourcen, die einem Prozess zur Verfügung stehen, in sogenannte Threads unterteilt werden, die dann auch parallel ablaufen. So kann ein Browser zum Beispiel im Hintergrund eine Datei herunterladen, während er im Vordergrund noch vom Benutzer bedienbar ist.

Das Problem ist nun, dass es verschiedene Ebenen gibt, auf denen man Dateisperren implementieren kann: Auf Prozessebene, auf Threadebene und auf Dateideskriptorebene. Wenn ein Sperrmechanismus auf Prozessebene definiert ist, dann heißt dies, dass ein Programm die Sperre innerhalb des Prozesses beliebig oft neu anfordern kann, ohne dass etwas passiert. Ist nun ein Prozess in zwei Threads unterteilt und Thread A hat mit so einem Mechanismus eine Datei gesperrt, kann Thread B problemlos eine Sperre anfordern, ohne, dass er warten müsste - die Sperre greift innerhalb des Prozesses nicht. Wenn nun dagegen eine Sperre auf Dateideskriptorebene implementiert ist (ein Dateideskriptor ist das Objekt - meist eine Zahl - das man vom Betriebssystem erhält, wenn man eine Datei öffnet, unter PHP ist dies zum Beispiel das Ergebnis von fopen), dann kann ein Prozess eine Datei zwar problemlos zweimal öffnen, aber selbst der gleiche Thread kann die Sperre nicht erneut anfordern, ohne auf sich selbst warten zu müssen (siehe den obigen Abschnitt über Deadlocks).

Wenn Dateisperren nun auf Dateideskriptorebene oder auf Threadebene implementiert sind, dann gibt es kein Problem: Threads können sich gegeinander abschotten, man muss lediglich die gleichen Dinge beachten, die man auch bei Prozessen beachten muss. Sind Dateisperren dagegen auf Prozessebene implementiert, wird es problematisch.

Unter Windows sind die Locking-Mechanismen auf Dateideskriptorebene implementiert. Daher ist es unter Windows möglich, Threads gegeneinander abzuschotten.

Unter UNIX-artigen Betriebssystemen hängt die Ebene, auf der die Sperren implementiert sind, von dem Mechanismus ab, den man verwendet. Folgende Tabelle illustriert den Zusammenhang für Linux, FreeBSD und Mac OS X. Dem Autor war es nicht möglich, andere UNIX-artige Betriebssysteme diesbezüglich zu testen, daher kann es dort anders aussehen!

Sperremechanismus Prozessebene? Threadebene? Dateideskriptorebene?
flock(2) Nein Nein Ja
lockf(3) Ja Nein Nein
fcntl(2) Ja Nein Nein

Das heißt: Mittels flock kann man auf UNIX-artigen Betriebssystemen (zumindest den hier getesteten) Threads gegeneinander abschotten. Sowohl PHP, Perl als auch Python verwenden im Normalfall flock(2) bei den hier vorgestellten Funktionen. Alle drei Sprachen emulieren flock(2) durch fcntl(2), falls das nicht zur Verfügung steht, was aber bei den drei hier betrachteten Betriebssystemen irrelevant ist. Allerdings erlaubt es Perl, auch bei Vorhandensein von flock(2) dieses mit fcntl(2) zu emulieren (dies kann man beim Kompilieren und Installieren von Perl festlegen). Das heißt: Mit dem in diesem Artikel vorgestellten Code sollten in der Regel auch bei der Verwendung von Threads keine Probleme auftreten.

fcntl(2) hat zusätzlich den Nachteil, dass sobald ein einziger Thread einen Dateideskriptor, der mit dieser Datei vernüpft war, schließt, werden alle Sperren, die auf diese Datei angelegt wurden, aufgehoben. Das heißt: Selbst wenn sich die Threads selbst bei einem Aufruf zufälligerweise gar nicht in die Quere kommen, kann dies dennoch dazu führen, dass durch das vorzeitige Aufheben der Sperre andere Programme in die Quere kommen.

nach obennach unten

PHP/Perl/Python und Webserver mit Threads

Wenn man selbst keine Threads verwendet (und zumindest PHP es auch nicht erlaubt, eigene Threads zu starten), sollte man meinen, dass diese Diskussion relativ akademisch ist. Dies ist aber nur insofern korrekt, als dass im Webumfeld Threads durchaus relevant sein könnten. Je nachdem, wie diese Scriptsprachen nämlich in den Webserver eingebunden sind, kann es sein, dass verschiedene Scripte in verschiedene Threads ablaufen. Denn wenn ein Webserver verwendet wird, der unterschiedliche Anfragen auf unterschiedliche Threads verteilt (z.B. Apache 2.x mit Worker-MPM oder Microsofts IIS), und die Scriptsprachen direkt in den Webserver integriert sind (beim Apache als Modul, unter dem IIS als ISAPI-Filter), dann laufen fast zeitgleich aufgerufene Scripte im gleichen Prozess in unterschiedlichen Threads, obwohl in den Scripten selbst Threads gar keine Berücksichtigung finden. Zu beachten ist, dass der Apache sehr häufig mit dem Prefork-MPM verwendet wird, das keine Threads, sondern separate Prozesse nutzt. Zudem sind die CGI-Varianten der jeweiligen Sprachen unabhängig vom Webserver nicht betroffen.

Wird nun ein Locking-Mechanismus verwendet, der nur auf Prozessebene implementiert ist, dann kommt es zu den oben genannten Problemen. Da die genannten Webservermodule die Requests voneinander abschotten, gibt es auch keine einfache Alternative, dass diese Threads normale Thread-Mechanismen wie Mutexes zur Synchronisation der Zugriffe nutzen.

Aus diesem Grund ist der Einsatz von Scriptsprachen in gethreadeten Webserverumgebungen mit Vorsicht zu genießen, wenn es darum geht, dass Dateien gesperrt werden sollen.

nach obennach unten

Java mit unterschiedlichen Threads, Servlets

Bei Java eignet sich der eingebaute Locking-Mechanismus nicht, um gegen Threads abzuschotten. Zum einen wird unter UNIX-artigen Betriebssystemen die fcntl(2)-Methode verwendet, die nur gegen Prozesse abschottet. Zum anderen garantiert Java auch unter Windows dieses Verhalten nicht. Zudem kann nach bisherigen Tests das Sperren einer Datei in einem anderen Thread dazu führen, dass das Sperren sich nicht normal verhält, d.h. es wird nicht auf das Freiwerden der Sperre gewartet, sondern lediglich eine bestimmte Exception geworfen, was die Nützlichkeit auch stark verringert.

Allerdings hat man unter Java in der Regel die Kontrolle über die Thread-Umgebung. Wenn man ein normales Programm schreibt, das selbst verschiedene Threads erzeugt, dann kann man die normalen Synchronisationsmechanismen in Java nutzen, um Dateizugriffe aufeinander abzustimmen.

Und bei Servlets wird zwar jeder Request in einem separaten Thread ausgeführt, allerdings wird die Servlet-Klasse selbst nur ein einziges Mal instanziert, und für jeden Request wird nur jeweils eine Methode in dem Thread aufgerufen. Zudem wird die Methode init des Servlets beim Starten ausgeführt. Daher ist es auch bei Servlets problemlos möglich, dass sich verschiedene Threads über normale Sychronisationsmechanismen abstimmen - zumindest solange es nur Servlets im gleichen Servlet-Context betrifft.

weiter Seite Programmiersprachenübergreifende Sperren

zurück Seite Deadlocks - Teergruben bei Dateisperren

Teil von SELFHTML aktuell Teil von Artikel Teil von Programmiertechnik Teil von Sperren von Dateien

© 2006 bereichsübergreifende Seite Impressum