25

使用以下文件读取代码:

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
    using (TextReader tr = new StreamReader(fileStream))
    {
        string fileContents = tr.ReadToEnd();
    }
}

和下面的文件写代码:

using (TextWriter tw = new StreamWriter(fileName))
{
    tw.Write(fileContents);
    tw.Close();
}

可以看到以下异常详细信息:

该进程无法访问文件“c:\temp\myfile.txt”,因为它正被另一个进程使用。

避免这种情况的最佳方法是什么?读者是否需要在收到异常后重试,还是有更好的方法?

请注意,阅读器进程使用FileSystemWatcher来了解文件何时更改。

另请注意,在这种情况下,我不是在寻找在两个进程之间共享字符串的替代方法。

4

8 回答 8

38

您可以打开一个文件进行写入并且只锁定写入权限,从而允许其他人仍然读取该文件。

例如,

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
   // Do your writing here.
}

其他文件访问只是打开文件进行读写,并允许读写共享。

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
   // Does reading  here.
}

如果您想确保读者将始终阅读最新文件,您将需要使用锁定文件来指示有人正在写入该文件(尽管如果不仔细实施,您可能会遇到竞争条件)或使确保在打开以读取和处理异常时阻止写入共享,以便您可以重试,直到获得独占访问权限。

于 2008-10-21T14:35:54.560 回答
7

如果您创建了一个命名的互斥锁,您可以在写入应用程序中定义互斥锁,并让读取应用程序等到互斥锁被释放。

所以在当前正在使用 FileSystemWatcher 的通知进程中,只需检查是否需要等待互斥锁,如果需要,它将等待,然后处理。

这是我发现的类似 Mutex 的 VB 示例,它应该很容易转换为 C#。

于 2008-10-21T14:37:14.853 回答
3

如果正在写入文件,请让您的进程检查文件的状态。您可以通过锁定文件的存在来做到这一点(即,这个其他文件的存在,可以是空的,防止写入主文件)。

然而,即使这也不是故障安全的,因为两个进程可能同时创建锁定文件 - 但您可以在提交写入之前检查这一点。

如果您的进程遇到锁定文件,则让它简单地休眠/等待,并在未来以预定义的时间间隔重试。

于 2008-10-21T14:38:05.337 回答
3

使用 FileShare.None 打开文件有什么特别的原因吗?这将防止文件被任何其他进程打开。

FileShare.Write 或 FileShare.ReadWrite 应该允许其他进程(受权限限制)在您读取文件时打开和写入文件,但是您必须在读取文件时注意文件在您下方的更改 - 只是缓冲打开时的内容可能会有所帮助。

然而,所有这些答案都是同样有效的——最好的解决方案取决于你试图对文件做什么:如果在保证它不会改变的同时读取它很重要,那么锁定它并处理随后的异常在您的编写代码中;如果同时读取和写入它很重要,则更改 FileShare 常量。

于 2008-10-21T14:51:10.333 回答
2

Mutex您可以为此使用对象。

于 2008-10-21T14:32:04.893 回答
2

读取器和写入器都需要重试机制。FileShare也应该设置为FileShare.read为 reader 和FileShare.none为 writer。这应该确保读取器在写入过程中不会读取文件。

阅读器(不包括重试)变为

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    using (TextReader tr = new StreamReader(fileStream))
    {
        string fileContents = tr.ReadToEnd();
    }
}

写入器(不包括重试)变为:

FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
using (TextWriter tw = new StreamWriter(fileStream))
{
    tw.Write(fileContents);
    tw.Close();
}
于 2008-10-21T15:02:26.737 回答
1

写入临时文件,完成写入后将文件重命名/移动到读者正在查找的位置和/或名称。

于 2008-10-21T14:44:29.083 回答
1

最好的办法是将应用程序协议放在文件传输/所有权传输机制之上。“锁定文件”机制是一种古老的 UNIX hack,已经存在了很长时间。最好的办法就是将文件“交给”读者。有很多方法可以做到这一点。您可以使用随机文件名创建文件,然后将该名称“提供”给阅读器。这将允许作者异步写入另一个文件。想想“网页”是如何工作的。一个网页有一个指向更多信息的“链接”,包括图像、脚本、外部内容等。服务器将那个页面交给你,因为它是你想要的“资源”的连贯视图。然后你的浏览器会去获取适当的内容,

This is the most resilient type of "sharing" mechanism to use. Write the file, share the name, move to the next file. The "sharing the name" part, is the atomic hand off that makes sure that both parties (the reader and the writer) agree that the content is "complete."

于 2013-05-07T20:21:12.697 回答