File.AppendAllText是否管理来自多个作者的冲突?
研究
我注意到MSDN documentation并没有真正提供任何位置,所以我决定我将反射(reflect)代码并看它能做什么。下面是从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)
因此,如果我有两个线程试图同时访问同一路径的Windows,Windows会怎么办?它将打开文件并缓冲写入操作,以便两个使用者都可以写入文件吗?还是我需要在对lock的调用周围利用锁对象和AppendAllText

最佳答案

关键是这种方法:

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可以很容易地做到这一点,但是除非您连续不断地写入文件(例如在日志记录中),否则这可能是过大的。

07-24 19:10