2013-04-18 9 views
13

Frage

Kann File.AppendAllText Kollisionen von mehreren Schreibern verwalten?Behandelt File.AppendAllText Kollisionen (d. H. Mehrbenutzer-Parallelität)?

Forschung

Ich bemerkte, dass die MSDN documentation nicht wirklich eine Position oder so schaffen, so dass ich beschloss ich, den Code reflektieren würde und nur sehen, was es tut. Im Folgenden finden Sie die aufgerufene Methode von File.AppendAllText:

private static void InternalAppendAllText(string path, string contents, Encoding encoding) 
{ 
    using (StreamWriter streamWriter = new StreamWriter(path, true, encoding)) 
    { 
     streamWriter.Write(contents); 
    } 
} 

und wie Sie sehen können nutzt es einfach eine StreamWriter. Also, wenn wir ein wenig tiefer in die graben, speziell den Konstruktor es verwendet, so finden wir, dass es letztlich dieser Konstruktor ruft:

internal StreamWriter(string path, bool append, Encoding encoding, int bufferSize, bool checkHost) : base(null) 
{ 
    if (path == null) 
    { 
     throw new ArgumentNullException("path"); 
    } 
    if (encoding == null) 
    { 
     throw new ArgumentNullException("encoding"); 
    } 
    if (path.Length == 0) 
    { 
     throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); 
    } 
    if (bufferSize <= 0) 
    { 
     throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); 
    } 
    Stream streamArg = StreamWriter.CreateFile(path, append, checkHost); 
    this.Init(streamArg, encoding, bufferSize, false); 
} 

mit den folgenden Werten:

path:  the path to the file 
append:  the text to append 
encoding: UTF8NoBOM 
bufferSize: 1024 
checkHost: true 

und weiter finden wir, dass Die base(null) Implementierung tut nichts wirklich, aber setzen Sie die InternalFormatProvider auf null. Also, wenn wir halten graben wir feststellen, dass CreateFile:

private static Stream CreateFile(string path, bool append, bool checkHost) 
{ 
    FileMode mode = append ? FileMode.Append : FileMode.Create; 
    return new FileStream(path, mode, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost); 
} 

ein FileStream mit diesen Parameterwerten erstellt:

path:   the path to the file 
mode:   FileMode.Append 
access:  FileAccess.Write 
share:  FileShare.Read 
bufferSize: 4096 
options:  FileOptions.SequentialScan 
msgPath:  just the file name of the path provided 
bFromProxy: false 
useLongPath: false 
checkHost: true 

ein so jetzt sind wir endlich immer irgendwo, weil wir das nutzen, sind dabei zu Windows-API, und das ist, wo die Frage wirklich beginnt, weil das FileStream::ctor eine Methode namens Init aufruft. Es ist eine ziemlich lange Methode, aber ich bin wirklich in einer Linie interessiert:

this._handle = Win32Native.SafeCreateFile(text3, 
    dwDesiredAccess, 
    share, 
    secAttrs, 
    mode, 
    num, 
    IntPtr.Zero); 

die natürlich CreateFile nennt, wo die Parameterwerte:

text3:   the full path to the file 
dwDesiredAccess: 1073741824 
share:   1 (FILE_SHARE_READ) 
secAttrs:   null 
mode:    4 (OPEN_ALWAYS) 
num:    134217728 | 1048576 (FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_POSIX_SEMANTICS) 

so, was würde von Windows tun, wenn ich hatten zwei Threads versucht, gleichzeitig für denselben Pfad auf diesen Anruf zuzugreifen? Würde es die Datei öffnen und die Schreibvorgänge zwischenspeichern, sodass beide Benutzer in die Datei schreiben können? Oder muss ich ein Lock-Objekt und lock um den Anruf zu AppendAllText nutzen?

Antwort

4

Der Schlüssel ist, diese Methode:

private static Stream CreateFile(string path, bool append, bool checkHost) 
{ 
    FileMode mode = append ? FileMode.Append : FileMode.Create; 
    return new FileStream(path, mode, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost); 
} 

Es ist mit FileShare.Read öffnen, was bedeutet, dass andere Threads oder Prozesse die Datei zum Lesen öffnen können, aber kein anderer Prozess/Thread es zum Schreiben öffnen kann.

Wahrscheinlich möchten Sie nicht, dass mehrere gleichzeitige Schreibvorgänge zulässig sind. Überlegen Sie, zwei sehr große Puffer zu schreiben. Es ist sehr wahrscheinlich, dass sie ineinander übergehen würden.

Also, ja ... wenn Sie mehrere Threads haben, die an diese Datei anhängen, müssen Sie den Zugriff wahrscheinlich mit einer Sperre synchronisieren.

Eine weitere Option, je nach Anwendung, wäre ein Verbraucher-Thread, der Text aus einer Warteschlange liest und an die Datei anfügt. Auf diese Weise hat nur ein Thread Zugriff auf die Datei. Andere Threads fügen die Nachrichten in eine Warteschlange ein, die vom Autorenthread bedient wird. Dies ist ziemlich einfach mit BlockingCollection zu tun, ist aber wahrscheinlich zu viel, es sei denn, Sie schreiben fortlaufend in die Datei (wie bei der Protokollierung).

+0

Das ist eine Idee! Mit ** einem Schreiber, der eine Warteschlange liest, ** wird das Problem drastisch vereinfacht. Ich habe mehr als ein Szenario in PROD wie dieses, aber man loggt, haben Sie ein Beispiel, das die 'BlockingCollection' zum Protokollieren praktisch verwendet? Wenn nicht, keine Sorge, ich kann es tun. –

+0

Mach dir keine Sorgen über das Beispiel mein Freund, es ist Kuchen. Eine kurze Frage. Du hast erwähnt, dass es wahrscheinlich übertrieben war, es sei denn, es war für etwas wie Holzfällerei. Liegt das an dem Overhead des Typs selbst (d. H. In Szenarien mit niedrigeren Transaktionen würde dies Leistungsprobleme verursachen)? –

+0

@MichaelPerrenoud - Wenn Sie es für die Protokollierung verwenden, abhängig davon, wie viel, bevorzugen Sie möglicherweise ein Protokollierungsframework wie "Log4Net", das Multithreading-Szenarien für Sie behandeln sollte http://StackOverflow.com/questions/1519211/multithread -safe-logging – keyboardP

7

Nur einer wird zum Schreiben gewinnen, und es wird der erste sein, alle nachfolgenden Versuche werden fehlschlagen, bis die Schreibsperre aufgehoben wird (dh der Puffer wird geleert und die Datei geschlossen) - jedoch könnte er gleichzeitig geöffnet sein (Berechtigungen abhängig).

Read - Erlaubt das nachfolgende Öffnen der Datei zum Lesen. Wenn dieses Flag nicht angegeben ist, wird jede Anforderung zum Öffnen der Datei zum Lesen (durch diesen Prozess oder einen anderen Prozess) fehlschlagen, bis die Datei geschlossen wird. Jedoch, selbst wenn dieses Flag angegeben wird, können weitere Berechtigungen möglicherweise noch erforderlich sein, um auf die Datei zuzugreifen.

+0

Einverstanden. Beachten Sie auch die using-Anweisung. Die Datei wird so schnell wie möglich geöffnet und geschlossen, so dass das Sicherheitsfenster klein ist. Aber es ist da. –

+0

@SteveWellens Ja, dieses Zeitfenster wäre in jeder Situation mit doppeltem Zugriff von Bedeutung, wie klein es auch ist, aber es hat auch eine Chance, nicht ein kleines Fenster zu sein. –

Verwandte Themen