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