2008-10-21 4 views
25

Mit dem folgenden Dateilesecode:Wenn in C# 2 Prozesse in derselben Datei lesen und schreiben, was ist der beste Weg, Prozesssperrausnahmen zu vermeiden?

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None)) 
{ 
    using (TextReader tr = new StreamReader(fileStream)) 
    { 
     string fileContents = tr.ReadToEnd(); 
    } 
} 

und dem folgenden Datei schreibt Code:

using (TextWriter tw = new StreamWriter(fileName)) 
{ 
    tw.Write(fileContents); 
    tw.Close(); 
} 

Folgende Ausnahme Details sind zu sehen:

Der Prozess kann nicht auf die Datei zugreifen kann 'c: \ temp \ myfile.txt', weil es von einem anderen Prozess verwendet wird.

Was ist der beste Weg, dies zu vermeiden? Muss der Leser nach Erhalt der Ausnahme erneut versuchen oder gibt es einen besseren Weg?

Beachten Sie, dass der Leserprozess einen FileSystemWatcher verwendet, um zu wissen, wann die Datei geändert wurde.

Beachten Sie auch, dass ich in diesem Fall bin nicht Suche nach alternativen Möglichkeiten der Teilung von Zeichenfolgen zwischen den 2 Prozessen.

Antwort

34

Sie können eine Datei zum Schreiben öffnen und nur den Schreibzugriff sperren, sodass andere Benutzer die Datei weiterhin lesen können.

Zum Beispiel

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read)) 
{ 
    // Do your writing here. 
} 

Andere Access-Datei öffnet einfach die Datei zum Lesen und Schreiben nicht und ermöglicht Lese-Schreib-Sharing.

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
    // Does reading here. 
} 

Wenn Sie sicherstellen möchten, dass die Leser immer ein up-to-date-Datei lesen, müssen Sie entweder eine Sperrdatei verwenden, die jemand anzeigt, in die Datei zu schreiben (obwohl Sie eine Race-Bedingung erhalten kann Wenn Sie dies nicht sorgfältig durchgeführt haben, oder stellen Sie sicher, dass Sie beim Öffnen die Schreibfreigabe blockieren, um die Ausnahme zu lesen und zu bearbeiten, so dass Sie es erneut versuchen können, bis Sie exklusiven Zugriff erhalten.

+0

Aber in diesem Fall, wenn ich die Funktionsweise richtig verstehe, könnten Sie eine unvollständige Datei lesen? –

+3

Das stimmt, ja. Das wird immer der Fall sein, wenn Sie mehr als einem Verbraucher erlauben, auf die Datei zuzugreifen. Die einzige Möglichkeit, dies zu erreichen und zu synchronisieren, ist entweder eine Sperrdatei zu verwenden oder die Zugriffsausnahme abzufangen und es erneut zu versuchen, bis Sie exklusiven Zugriff erhalten. –

+0

Ich bin ein wenig verwirrt, weil dies der akzeptierten Antwort auf diese Frage zu widersprechen scheint: http://stackoverflow.com/questions/25097773/system-io-filestream-fileaccess-vs-fileshare, weil es das für FileAcces.ReadWrite angibt Sie müssen FileShare.None verwenden. – user2481095

2

Sie können hierfür ein Objekt Mutex verwenden.

+0

Danke für die schnelle Antwort. Was ist, wenn sich die Prozesse auf unterschiedlichen Systemen befinden und die Datei über das Netzwerk freigegeben wird? – Iain

+0

Hehe, das wird dann nicht funktionieren. Verwenden Sie eine Null-Byte- "Lock" -Datei wie Linux-Dienste dann :) – leppie

+0

Oder besser noch einen Dienst schreiben, und verwenden Sie es, um den Zugriff auf die Datei mit einem Mutex zu steuern, Dies sollte mit einem Beispiel und einem globalen Mutex verursacht werden "Populäre" Antwort auf diese Frage ist im Moment wirklich riskant Code. –

6

Wenn Sie einen benannten Mutex erstellen, können Sie den Mutex in der Schreibanwendung definieren und die Leseanwendung warten lassen, bis der Mutex freigegeben wird.

Überprüfen Sie also im Benachrichtigungsvorgang, der gerade mit dem FileSystemWatcher arbeitet, einfach, ob Sie auf den Mutex warten müssen. Wenn Sie dies tun, wird er warten und dann verarbeiten.

Hier ist eine VB example of a Mutex wie diese, die ich gefunden habe, sollte es einfach genug sein, um zu C# zu konvertieren.

2

Erhalten Sie Ihren Prozess, um den Status der Datei zu überprüfen, wenn sie in geschrieben wird. Sie können dies durch das Vorhandensein einer Sperrdatei erreichen (d. H. Das Vorhandensein dieser anderen Datei, die leer sein kann, verhindert das Schreiben in die Hauptdatei).

Auch dies ist jedoch nicht fehlersicher, da die beiden Prozesse die Sperrdatei zur gleichen Zeit erstellen können - aber Sie können dies überprüfen, bevor Sie den Schreibvorgang festschreiben.

Wenn Ihr Prozess auf eine Sperrdatei stößt, lassen Sie ihn einfach in den Ruhezustand versetzen/warten und versuchen Sie es erneut in einem vordefinierten Intervall in der Zukunft.

1

In eine temporäre Datei schreiben, nach dem Schreiben umbenennen/verschieben Sie die Datei an den Ort und/oder Namen, nach dem der Leser sucht.

3

Gibt es einen besonderen Grund für das Öffnen der Datei mit FileShare.None? Dadurch wird verhindert, dass die Datei von einem anderen Prozess geöffnet wird.

FileShare.Write oder FileShare.ReadWrite sollte dem anderen Prozess (vorbehaltlich der Berechtigungen) erlauben, zu öffnen und in die Datei zu schreiben, während Sie es lesen, jedoch müssen Sie darauf achten, dass sich die Datei während des Lesens unter Ihnen ändert Es kann einfach helfen, den Inhalt beim Öffnen zu puffern.

Alle diese Antworten sind jedoch gleichermaßen gültig - die beste Lösung hängt davon ab, was genau Sie mit der Datei versuchen: Wenn es wichtig ist, es zu lesen, während es garantiert nicht ändert, dann sperren und Behandle die nachfolgende Ausnahme in deinem Schreibcode; Wenn es wichtig ist, gleichzeitig zu lesen und zu schreiben, ändern Sie die FileShare-Konstante.

2

Der Leser und der Schreiber benötigen beide Wiederholungsmechanismen. Auch FileShare sollte auf FileShare.read für die Leser und FileShare.none für den Schreiber eingestellt werden. Dies sollte sicherstellen, dass die Leser die Datei während des Schreibens nicht lesen.

Der Leser (ohne Wiederholungs) wird

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
    using (TextReader tr = new StreamReader(fileStream)) 
    { 
     string fileContents = tr.ReadToEnd(); 
    } 
} 

Der Schreiber (ohne Wiederholungs) wird:

FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); 
using (TextWriter tw = new StreamWriter(fileStream)) 
{ 
    tw.Write(fileContents); 
    tw.Close(); 
} 
1

Das Beste, was zu tun ist, ist ein Anwendungsprotokoll auf einer Datei ablegen Transfer-/Eigentumsübertragungsmechanismus. Der "Lock-File" -Mechanismus ist ein alter UNIX-Hack, der schon seit Ewigkeiten existiert. Am besten ist es, die Datei einfach an den Leser zu "übergeben". Es gibt viele Möglichkeiten, dies zu tun. Sie können die Datei mit einem zufälligen Dateinamen erstellen und diesen Namen dann dem Leser "geben". Das würde dem Schreiber ermöglichen, asynchron eine andere Datei zu schreiben. Denken Sie daran, wie die "Webseite" funktioniert. Eine Webseite hat einen "Link" zu mehr Information darin, für Bilder, Skripte, externen Inhalt usw. Der Server übergibt Ihnen diese Seite, weil es eine zusammenhängende Ansicht der "Ressource" ist, die Sie wollen. Ihr Browser geht dann und erhält den entsprechenden Inhalt, basierend auf der Seitenbeschreibung (der HTML-Datei oder anderem zurückgegebenen Inhalt), und überträgt dann, was er benötigt.

Dies ist die am meisten belastbare Art des "Teilen" -Mechanismus. Schreiben Sie die Datei, teilen Sie den Namen, gehen Sie zur nächsten Datei. Der "Teilen des Namens" -Teil ist die atomare Übergabe, die sicherstellt, dass beide Parteien (der Leser und der Schreiber) zustimmen, dass der Inhalt "vollständig" ist.

Verwandte Themen