【问题标题】:Reading a file without preventing other processes from reading it at the same time在不阻止其他进程同时读取文件的情况下读取文件
【发布时间】:2011-06-30 18:04:16
【问题描述】:

我正在用 C#/.NET 制作一个小应用程序,用于监视文件的创建,并在创建文件时获取其内容,对其进行解析并将其写入另一个文件。

到目前为止一切正常。但问题是:还有另一个进程也在监视这个文件。我的进程只是读取文件,而第二个进程读取它然后删除它。

我的应用程序正在完成它的工作,但是当它读取文件时,其他进程无法读取它并完全崩溃(不是我制作的,也没有修复它的资源)。

我的应用程序运行得非常快,其他打开文件的时间很短,以获取内容并将其放入变量中,以便它可以更快地关闭文件,然后解析变量中文件的内容。

我显然不知道如何,但我希望能够读取文件并让其他人同时读取文件而不会出现任何问题。是否可以?我仍然认为文件在其他应用完成解析后被删除的事实会有问题......

有什么建议或想法吗?

非常感谢!

【问题讨论】:

  • 听起来问题不在于读取,而是其他进程在锁定读取访问权限时尝试修改(删除)文件。整个设置听起来很粗略,因为您只是希望您的进程首先注意到该文件。任何数量的因素都可能使这非常不可靠。
  • 您是否可以控制创建文件的过程?如果是这样,让它创建两个文件,然后读取一个未被您无法控制的进程使用的文件。
  • @jamietre :我知道粗略的因素,不用担心;)但这只是为了收集真实的使用数据以用于我现在正在开发的新版本。所以可靠性不是很重要。 @Michael:不,我没有任何控制权,如果我有,我就不会制作复制数据的程序:P 谢谢!

标签: c# .net timer filesystemwatcher


【解决方案1】:

您可以按如下方式打开该文件,以确保不会将其与其他进程锁定:

using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{
  // do your stuff
}

但如果其他进程试图以独占模式打开它,它不会有帮助,它仍然会崩溃。除了修复其他进程的代码之外,没有其他方法可以处理。

【讨论】:

  • 最好只使用var file = File.ReadAllBytes(path),这样文件在操作期间不会打开(除非有写操作)。
  • @Erik Phillips - 很好的建议,但是,MSDN 页面没有提到文件以什么模式打开,据我所见 (msdn.microsoft.com/en-us/library/…)。所以我这样做是为了确保我们不会锁定其他潜在读者的文件。如果您知道某处记录了 File.ReadAllBytes 使用 FileShare.ReadWrite 作为共享模式,那么我同意您的看法。
  • 另一个进程似乎以独占模式打开文件。所以无论我尝试什么都会导致其他应用程序崩溃......我开始认为这几乎 - 不可能 - 这样做......
  • @dcp,没有记录,但 Telerik 的 JustDecompile 显示 File.ReadAllBytes 以 FileMode.Open、FileAccess.Read 和 FileShare.Read 打开。
【解决方案2】:

KISS:您能否在第一个程序未查看但您的软件正在查看的位置创建文件 - 当您完成处理后,您然后将其移动到第一个程序所在的当前位置 看吗?

否则: 您将产生争用,因为这将是一场竞赛,看哪个进程实际上首先“注意到”文件并开始工作。

我假设您也无法控制创建文件的过程?

在这种情况下,您可能会查看PsSuspendPauseSp - 如果您可以通过暂停它来控制另一个进程,直到您准备好(完成文件),那么这可能是可行的。不确定这会有多强大。

仍然存在“注意到”文件并执行操作(无论它是什么)的潜在竞争条件 - 保持另一个进程永久暂停直到您希望它运行(或杀死它并启动它)是唯一完全在约束范围内实现您想要的确定性方法。

【讨论】:

    【解决方案3】:

    如果您使用的是 NTFS 驱动器(很可能),那么您可以创建指向该文件的硬链接。本质上,这会复制文件而不会实际创建副本。您可以使用硬链接读取文件。其他进程可以删除文件,这只会删除他们到文件的链接。这将使文件留在原处供您阅读。当你的程序读完文件后,它可以删除硬链接,文件系统会看到这两个链接都被删除了,它会删除文件本身。

    这可以通过command line 完成

    fsutil hardlink create <NewFileName> <ExistingFileName>
    

    或者您可以在 Windows API 中 P/Invoke CreateHardLink 函数。

    【讨论】:

    • 非常简洁的解决方案 - 虽然仍然存在竞争条件 - 您无法保证在原始程序“注意到”文件并开始读取/删除文件之前创建硬链接的任何内容都会发生循环。
    • @Chris B - 这是真的。我假设 OP 中给出的所有架构决策都是固定不变的。显然,基本设计本身就有缺陷,正确的解决方案是尽可能更改它,正如您所建议的那样。
    【解决方案4】:

    您能否创建另一个名为 .reading 的空零字节文件,该文件具有相同的名称但扩展名为“reading”。然后一旦第一个进程完成读取文件,将 .reading 重命名为 .done ,第二个进程可以检查 .done 文件并删除原始文件,因为 .done 和原始文件具有相同的名称但扩展名不同?

    【讨论】:

      【解决方案5】:

      @Prashant 的回复给了我这个灵感,而且非常相似,但我相信会解决你的问题。

      如果其他进程必须匹配某个文件名模式

      • 将文件重命名为 不会先匹配,非常便宜/快速 操作
      • 完成后重命名

      如果它匹配给定文件夹中的每个文件

      • 将其移动到另一个文件夹(在大多数文件系统中也是一个非常便宜的操作)
      • 完成后将其移回。

      如果其他进程已经锁定了您的文件(即使是为了读取),那么您的进程将会失败,您可以让它变得优雅。如果没有,你应该是安全的。

      当然,仍然存在竞争条件的可能性,但这应该比你正在做的更安全。

      【讨论】:

        猜你喜欢
        • 2023-04-03
        • 1970-01-01
        • 2011-12-02
        • 2016-10-08
        • 1970-01-01
        • 2022-10-20
        • 2013-08-11
        • 1970-01-01
        • 2020-04-01
        相关资源
        最近更新 更多