2013-04-18 40 views
13

問題

File.AppendAllText是否管理來自多個作者的衝突?File.AppendAllText是否管理衝突(即多用戶併發)?

研究

我注意到MSDN documentation並沒有真正提供一個位置無論哪種方式,所以我決定我要反映的代碼,只是看看會發生什麼。下面是從File.AppendAllText調用方法:

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

,正如你可以看到它只是利用了StreamWriter。所以,如果我們深入一點成,特別是它採用的構造,我們發現,它最終調用此構造函數:

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); 
} 

具有以下值:

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

,並進一步我們發現, base(null)的實現並沒有做任何事情,只是將InternalFormatProvider設置爲null。因此,如果我們不斷挖掘我們發現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); 
} 

創建這些參數值的FileStream

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 

的所以現在我們終於取得了一些進展,因爲我們要充分利用Windows API,這是問題真正開始的地方,因爲FileStream::ctor調用名爲Init的方法。這是一個相當長的方法,但我在一條線很感興趣:

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

當然,它調用CreateFile,其中參數值是:

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) 

這樣,這將視窗做,如果我有兩個線程試圖同時訪問同一路徑上的該呼叫?它會打開文件並緩衝寫入,這樣兩個使用者都可以寫入文件嗎?或者我需要利用鎖定對象和lock圍繞呼叫AppendAllText

回答

4

最關鍵的是這種方法:

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); 
} 

它打開與FileShare.Read,這意味着其他線程或進程可以打開文件進行讀取,但沒有其他進程/線程可以打開它進行寫入。

你可能不希望它允許多個併發的作者。考慮編寫兩個非常大的緩衝區。它們很可能最終會被交織。

所以,是的......如果你有多個線程可能附加到該文件,你需要同步訪問,可能與鎖。

根據您的應用程序,另一種選擇是讓消費者線程從隊列中讀取文本並追加到文件中。這樣,只有一個線程可以訪問該文件。其他線程將這些消息放入寫入程序線程服務的隊列中。這與BlockingCollection很容易做到,但可能是過度殺毒,除非您連續寫入文件(如在日誌記錄中)。

+0

現在,這是一個想法!讓**一名作者閱讀隊列**大大簡化了問題。在這樣的PROD中,我有不止一種場景,但其中一種是日誌記錄,您是否有一個方便使用'BlockingCollection'進行日誌記錄的例子?如果不是,不用擔心,我可以做到。 –

+0

不要擔心我的朋友的例子,這是蛋糕。一個簡單的問題,但。你提到它可能是過度殺傷,除非它是用於記錄的東西。這是因爲類型本身的開銷(即在較低的事務情況下會導致性能問題)? –

+0

@MichaelPerrenoud - 如果您使用它來進行日誌記錄,取決於多少,您可能更喜歡使用日誌框架,例如'Log4Net',它應該爲您處理多線程場景http://stackoverflow.com/questions/1519211/multithread -safe-logging – keyboardP

7

只有一個會勝出寫入,並且它將是第一個,任何後續嘗試都會失敗,直到寫入鎖定被釋放(即緩衝區被刷新並且文件關閉) - 但是,它可以同時打開以供讀取(取決於權限)。

Read - 允許隨後打開文件供閱讀。如果未指定此標誌 ,則在文件關閉之前,打開文件以供讀取(通過此 進程或其他進程)的任何請求都將失敗。 但是,即使指定了此標誌,訪問該文件仍需要額外的權限 。

+0

同意。另請注意使用聲明。該文件儘快打開和關閉,因此漏洞窗口很小。但它在那裏。 –

+0

@SteveWellens是的,在任何雙重訪問的情況下,時間窗口都是重要的,儘管它很小,但它也有可能不是一個小窗口。 –