C# 文件共享未按预期工作
我有一个文件共享问题,我的进程试图读取一个日志文件,而该文件目前仍由NLog打开。在诊断这个问题时,我发现了一些令人惊讶的事情。以下操作失败:C# 文件共享未按预期工作,c#,.net,file-sharing,file-security,C#,.net,File Sharing,File Security,我有一个文件共享问题,我的进程试图读取一个日志文件,而该文件目前仍由NLog打开。在诊断这个问题时,我发现了一些令人惊讶的事情。以下操作失败: using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileA
using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.Read))
{
}
第二个FileStream
构造函数调用失败,原因是:
System.IO.IOException was unhandled
Message=The process cannot access the file 'c:\...\test.file' because it is being used by another process.
Source=mscorlib
StackTrace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
尽管第一个FileStream
表明它愿意共享读取。我发现更令人惊讶的是,这是有效的:
using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}
嗯,是的,在打开第二个流时请求更多访问实际上绕过了这个问题。我完全搞不懂为什么会这样,只能假设我误解了什么。我已经通读了API文档,但它们只是支持我当前的心智模型,说明它应该如何工作,而不是如何工作
以下是来自以下网站的一些支持性引用:
此枚举的典型用途是定义两个进程
可以同时读取同一文件。例如,如果文件是
如果指定了“已打开并读取”,则其他用户可以打开该文件进行读取
阅读而非写作
这里还有一块宝石:
以下FileStream构造函数打开现有文件并授予
对其他用户的只读访问(只读)
FileStream s2=newfilestream(名称,FileMode.Open,FileAccess.Read,FileShare.Read)代码>
有人能解释一下这种行为吗。我正在.NET 4%Windows XP上测试这个问题。您通过的第四个参数
分享
确定进程如何共享文件的常量
确定其他人可以以何种模式打开文件。因此,很明显,当您试图以文件共享模式“读取”打开文件,并且已经以写入模式打开同一文件时,操作失败。实际发生的情况是,fileStream2
无法更改对已通过fileStream1
打开以进行写入(或追加)的文件的后续访问
fileStream2
将成功打开该文件,留下一个FileShare.Read
作为“遗留”文件供后续访问,只有在没有已对其具有Write
文件访问权限的进程时。更重要的是,在我们的示例中,我们讨论的是相同的过程。从另一个文件流修改文件流的属性没有多大意义,不是吗
也许下面的比较更能说明这一点:
// works
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite))
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}
// fails
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}
在我看来,FileShare.Read的描述短语是:
允许随后打开文件进行读取
应理解为
对文件的后续访问仅限于读取,
包括对现有锁的访问
[更新]
我还没有分析过代码,但这两个链接似乎可以让我们了解构造函数的内部功能:
我想我已经在的文档中找到了答案
在讨论dwShareMode
参数时,它说:
文件共享\u读取0x00000001
启用文件或设备上的后续打开操作以请求读取访问。
否则,如果其他进程请求读取访问,则无法打开文件或设备。
如果未指定此标志,但文件或设备已打开以进行读取访问,则函数将失败
文件共享写入0x00000002
启用文件或设备上的后续打开操作以请求写入访问权限。
否则,如果其他进程请求写访问权限,则无法打开文件或设备。
如果未指定此标志,但文件或设备已打开以进行写访问,或者具有具有写访问权限的文件映射,则该功能将失败。
这从根本上改变了我对文件共享工作原理的理解
var fileStream2 = new FileStream(..., FileShare.Read)
这让许多程序员大吃一惊。每个人都认为这增加了读共享。它没有,原始文件访问请求已经允许读取并再次指定它不会改变任何东西。相反,它拒绝写共享。这是行不通的,因为有人已经有写权限了。并且正在使用它,您不能删除该权限。因此,访问该文件的请求将失败
您必须包括FileShare.Write。它不是第三个,而是第四个。和FileShare.Read
允许随后打开文件进行读取。FileShare.Read-“允许随后打开文件进行读取”-但不允许写入。您已经打开了文件以写入该共享模式禁止的内容。这个函数应该做什么?顺便问一下,关于“后续”你从哪里得到的?我的答案中引用的是MSDN,这里没有关于后续的内容。在对FileStream本身的描述中,没有关于后续过程的内容。甚至更多的标志源于WinAPi,并且始终与所有进程一起使用。(如果您在读取文件时禁止修改文件,那么允许将其修改给以前打开过它的人是没有意义的)。所以这在enum中不是很好的描述。尽管读了几遍你的答案,但仍然没有点击。我发现很难相信API文档是完全错误的。例如,“例如,如果打开了一个文件并指定了Read
,其他用户可以打开该文件进行阅读,但不能进行写入。”这正是我在示例中试图做的,但它不起作用。@KentBoogaart:我不知道它是否相关(我也不明白),但关键短语可能是“其他用户”。对“其他用户”更好的测试应该是多线程测试。@Chris:我真正的场景是多线程测试(试图在后台诊断线程上收集日志)。此外,文档在描述共享行为时明确表示“通过此流程或其他流程”。这很公平。我想看了一下这个(尽管我需要一份重构的副本来看看.NET代码中这是多么真实)是