Meine Webanwendung gibt eine Datei aus dem Dateisystem zurück. Diese Dateien sind dynamisch, daher habe ich keine Möglichkeit, die Namen zu kennen, oder wie viele von ihnen es geben wird. Wenn diese Datei nicht existiert, erstellt die Anwendung sie aus der Datenbank. Ich möchte vermeiden, dass zwei verschiedene Threads die gleiche Datei zur gleichen Zeit neu erstellen oder dass ein Thread versucht, die Datei zurückzugeben, während ein anderer Thread es erstellt.
Ich möchte auch kein Element sperren, das für alle Dateien gleich ist. Daher sollte ich die Datei nur beim Erstellen sperren.
Also möchte ich eine Datei sperren, bis ihre Wiederherstellung abgeschlossen ist, wenn ein anderer Thread versucht, auf sie zuzugreifen ... muss sie warten, bis die Datei entsperrt ist.
Ich habe über FileStream.Lock gelesen, aber ich muss die Dateilänge kennen und es wird nicht verhindern, dass der andere Thread versucht, die Datei zu lesen, so dass es für meinen speziellen Fall nicht funktioniert.
>Ich habe auch über FileShare.None gelesen, aber es wird eine Ausnahme ausgelöst (welcher Ausnahmetyp?), wenn ein anderer Thread / Prozess versucht, auf die Datei zuzugreifen ... also sollte ich einen "Versuch es erneut, während es fehlschlägt" entwickeln "weil ich die Ausnahmegeneration vermeiden möchte ... und ich mag diesen Ansatz nicht zu sehr, obwohl es vielleicht keinen besseren Weg gibt.
Der Ansatz mit FileShare.None wäre das mehr oder weniger:
%Vor%Aber ich mag die Tatsache nicht, dass ich Ausnahmen abfangen, mehrere Male versuchen muss und eine ungenaue Zeitmenge abwarten muss: |
Sie können das umgehen, indem Sie das FileMode.CreateNew-Argument für den Stream-Konstruktor verwenden. Einer der Threads wird verlieren und herausfinden, dass die Datei bereits eine Mikrosekunde zuvor von einem anderen Thread erstellt wurde. Und bekomme eine IOException.
Es muss sich dann drehen und darauf warten, dass die Datei vollständig erstellt wird. Welche erzwingen Sie mit FileShare.None. Hier Ausnahmen zu fangen ist egal, es dreht sich sowieso. Es gibt keine andere Problemumgehung dafür, außer Sie P / Invoke.
Ich denke, dass ein richtiger Ansatz der folgende wäre: Erstellen Sie eine Zeichenfolge, in der Sie den aktuellen Dateinamen speichern also würde ein Thread die Datei zur Zeit verarbeiten, so etwas wie
%Vor%Können Sie identifizieren, welche Dateien erstellt werden?
Sagen Sie, jede dieser Dateien entspricht einer eindeutigen ID in Ihrer Datenbank. Sie erstellen einen zentralen Speicherort (Singleton?), Wo diese IDs mit etwas gesperrt werden können (Dictionary). Ein Thread, der zu einer dieser Dateien lesen / schreiben muss, macht folgendes:
%Vor%Natürlich haben Threads, die diesem genauen Sperrprotokoll nicht folgen, Zugriff auf die Datei.
Nun ist das Sperren über ein Singleton-Objekt sicherlich nicht ideal, aber wenn Ihre Anwendung globale Synchronisation benötigt, dann ist dies ein Weg, dies zu erreichen.
Deine Frage hat mich wirklich zum Nachdenken gebracht.
Anstatt jeden Thread für den Dateizugriff verantwortlich zu machen und sie blockieren zu lassen, was passiert, wenn Sie eine Warteschlange mit Dateien verwendet haben, die persistent sein müssen, und einen einzelnen Hintergrund-Worker-Thread aus der Warteschlange nehmen und beibehalten?
Während der Hintergrund-Worker wegdreht, können die Webanwendungs-Threads die DB-Werte zurückgeben, bis die Datei tatsächlich existiert.
Ich habe ein sehr einfaches Beispiel auf GitHub gepostet.
Fühlen Sie sich frei, es zu versuchen und lassen Sie mich wissen, was Sie denken.
Zu Ihrer Information, wenn Sie git nicht haben, können Sie svn verwenden, um es http://svn.github.com/statianzo/MultiThreadFileAccessWebApp
Warum benutzen Sie nicht einfach die Datenbank - z. Wenn Sie eine Möglichkeit haben, einen Dateinamen mit den Daten aus der enthaltenen Datenbank zu verknüpfen, fügen Sie einfach einige Informationen zur Datenbank hinzu, die angibt, ob eine Datei mit diesen Informationen aktuell existiert und wann sie erstellt wurde, wie alt die Informationen in der Datei sind Wenn ein Thread einige Informationen benötigt, überprüft er die Datenbank, um festzustellen, ob diese Datei existiert, und wenn nicht, schreibt sie eine Zeile in die Tabelle, die besagt, dass sie die Datei erstellt. Wenn es fertig ist, wird diese Zeile mit einem booleschen Wert aktualisiert, der besagt, dass die Datei für andere Benutzer bereit ist.
das Schöne an diesem Ansatz - alle Ihre Informationen sind an einem Ort - so können Sie schöne Fehlerbeseitigung machen - z. Wenn der Thread, der die Datei erstellt, aus irgendeinem Grund fehlerhaft wird, kann ein anderer Thread mitkommen und beschließen, die Datei neu zu schreiben, da die Erstellungszeit zu alt ist. Sie können auch einfache Stapelbereinigungsprozesse erstellen und genaue Daten darüber erhalten, wie häufig bestimmte Daten für eine Datei verwendet werden und wie oft Informationen aktualisiert werden (indem Sie die Erstellungszeiten usw. beachten). Außerdem müssen Sie nicht viele Suchvorgänge in Ihrem Dateisystem durchführen, da verschiedene Threads überall nach verschiedenen Dateien suchen - insbesondere dann, wenn Sie sich mehrere Front-End-Computer auf einer gemeinsamen Festplatte suchen.
Die knifflige Sache - Sie müssen sicherstellen, dass Ihre Datenbank das Sperren auf Zeilenebene für die Tabelle unterstützt, in die die Threads schreiben, wenn sie Dateien erstellen, weil andernfalls die Tabelle selbst gesperrt werden könnte, was dies unakzeptabel langsam machen könnte.
>Die Frage ist alt und es gibt bereits eine deutliche Antwort. Trotzdem möchte ich eine einfachere Alternative veröffentlichen.
Ich denke, wir können die lock-Anweisung für den Dateinamen wie folgt direkt verwenden:
%Vor%Im Allgemeinen ist es aufgrund von String Interning eine schlechte Idee, einen String zu sperren. Aber in diesem speziellen Fall sollte es sicherstellen, dass niemand anders auf dieses Schloss zugreifen kann. Verwenden Sie einfach die gleiche Sperrzeichenfolge, bevor Sie versuchen, zu lesen. Hier arbeitet Praktikum für uns und nicht gegen.
PS: Der Text 'FileLock' ist nur irgendein willkürlicher Text, um sicherzustellen, dass andere Zeichenketten-Dateipfade nicht betroffen sind.
Tags und Links .net c# multithreading synchronization filestream