在C#中以独占模式打开文件

22

我希望以独占模式打开一个文件进行读取,如果该文件已被其他进程/线程打开,则希望收到异常。我尝试了以下代码,但没有起作用,即使我打开了foo.txt,我仍然可以访问Console.WriteLine语句。有任何想法吗?

static void Main(string[] args)
{
    using (Stream iStream = File.Open("c:\\software\\code.txt", FileMode.Open,
    FileAccess.Read, FileShare.None))
    {
        Console.WriteLine ("I am here");
    }

    return;
}

你是怎么打开它的?即使我打开了foo.txt。 - Mehrdad Afshari
是的,Mehrdad。我打开了它,然后执行了我的程序,仍然可以读取Console.WriteLine语句。有什么想法吗? - George2
1
用记事本?记事本在运行时不会锁定文件。它会将文件读入内存,并在保存时重新打开以进行写入。 - Mehrdad Afshari
我认为为了达到我的目的(在其他进程/线程打开此文件时不对该文件进行操作),我需要检查文件是否已经被打开,是吗? - George2
5个回答

26

您正在做正确的事情。也许只是您测试方法不正确。您应该使用 打开时锁定文件 的程序来打开它。记事本无法实现这一点。您可以运行应用程序两次以查看:

static void Main(string[] args)
{
    // Make sure test.txt exists before running. Run this app twice to see.
    File.Open("test.txt", FileMode.Open, FileAccess.Read, FileShare.None);
    Console.ReadKey();
}

谢谢Mehrdad,有没有办法检查一个文件是否已经被其他线程/进程打开了? - George2
谢谢Mehrdad,我已经测试过你是正确的。我的情况是,一个线程正在将数据序列化(使用XML序列化)到文件中,另一个读取线程正在从该文件中读取数据。我不希望读取线程在XML序列化过程中操作此文件,有什么想法吗? - George2
这已经实现了。如果一个线程正在从文件中读取数据,那么你会得到 IOException 异常;如果在你写入文件时读取线程尝试打开该文件,则读取线程也会收到 IOException 异常。 - Mehrdad Afshari
哦,你有很多文件?你考虑过使用存储引擎吗?可能是关系型数据库管理系统... - Mehrdad Afshari
它确实可以防止两个操作同时进行。问题在于,这种解决方案并不是阻塞式的。它会在访问被锁定的文件的线程上抛出异常。这就是为什么我建议使用lock(someObject) { }策略来手动同步线程。 - Mehrdad Afshari
显示剩余6条评论

5

通过编写一个简单的控制台模式程序来测试它,该程序打开文件,然后等待:

static void Main(string args[])
{
    using (FileStream f = File.Open("c:\\software\\code.txt", FileMode.Open, FileAccess.Read, FileShare.None))
    {
        Console.Write("File is open. Press Enter when done.");
        Console.ReadLine();
    }
}

从命令行(或Visual Studio的另一个实例)运行该程序,然后运行您的程序。这样,您可以尝试不同的FileMode和FileShare值,以确保您的程序在所有情况下都能正确响应。

而且,不需要先检查文件是否已经打开。如果文件已经打开,您的代码应该抛出异常。因此,您只需要处理该异常即可。


嗨,吉姆,我的情况是,一个线程正在复制文件(使用File.Move方法),另一个线程正在从文件中读取。我不希望读取器线程在复制过程中读取文件的部分内容(这就是为什么我想要独占打开模式),我发布的代码解决了这个问题吗? - George2
是的,您的代码解决了这个问题。如果File.Move正在复制文件,则不会打开该文件。 - Jim Mischel
Jim,我怎么知道任何(!)程序是否正在打开它?无论是记事本还是其他什么。或者我怎么知道它现在是否正在被复制? - user266003
@Grienders:如果在此代码示例中调用File.Open时出现访问冲突异常,则表示该文件当前已被另一个程序打开。但是,您无法确定是否有人使用记事本查看该文件,因为记事本会打开文件,将其读入内存,然后关闭文件。如果文件正在被复制,则执行复制的程序将使其保持打开状态。 - Jim Mischel

3

你所做的是正确的。

如果您需要查看已打开的所有文件,则可以通过NtQuerySystemInformation查看。

您可以从http://www.codeproject.com/KB/shell/OpenedFileFinder.aspx获取想法,该网站获取目录中打开的所有文件... 可以将其扩展到单个文件,无论是否已打开...


谢谢lakshmanaraj,我的情况是,一个线程正在将数据序列化(使用XML序列化)到文件中,另一个读取线程正在从该文件中读取。我不希望读取线程在XML序列化过程中操作此文件,有什么想法吗? - George2
您可以通过设置信号量来锁定文件或执行互斥操作。 - lakshmanaraj
文件名是任意的,因此很难锁定。更多细节,我使用写线程从远程机器复制任意文件到本地机器,然后读取线程将扫描文件夹并读取新文件的信息。 - George2
我的情况是,一个线程正在复制文件(使用File.Move方法),另一个线程正在从文件中读取。我不希望读取器线程在复制过程中读取文件的部分内容(这就是为什么我想要独占打开模式),我发布的代码解决了这个问题吗? - George2
是的,在复制操作期间,正在写入的文件会自动锁定,因此您的代码将无法从文件中读取。但是,您仍然可以检查状态(文件),并且struct stat->st_mode将给出文件的共享模式,以确定是否可以访问该文件。 - lakshmanaraj

2
我建议使用FileAccess.ReadWrite成员,因为有些文件可能已经打开,但允许你对文件进行Read访问。然而,我猜测在非异常情况下,所有以Read/Write方式打开的文件都不会允许你的代码向文件中写入内容。
当然(正如Mehrdad已经解释的那样),如果你使用像记事本这样的编辑器来打开文件进行测试,你将无法限制访问,因为记事本根本不会锁定文件。

谢谢Cerebrus,你是正确的,我正在使用记事本打开它并运行我的程序进行测试。有什么想法可以检查文件是否已经打开(例如,我希望我的代码能够检查记事本是否已经打开了它)? - George2
不,我认为没有其他方法...除非在随后尝试读取文件的代码中捕获IOException。 - Cerebrus
我的情况是,一个线程正在将数据序列化(使用XML序列化)到文件中,另一个读取线程正在从该文件中读取。我不希望读取线程在XML序列化过程中操作此文件,有什么好的解决方法吗? - George2

2

仅当另一个进程已打开文件但未允许其共享读取时,FileShare.None 才能正常工作。

像记事本和Visual Studio这样的程序不会锁定文本文件。


谢谢Jakob,有没有办法检查文件是否已被其他线程/进程打开? - George2
嗨,Jakob,我的情况是,一个线程正在将数据进行序列化(使用XML序列化)到文件中,另一个读取线程正在从该文件中读取。我不希望读取线程在XML序列化过程中对该文件进行操作,有什么想法吗? - George2
你是如何进行序列化的?在进行序列化时,必须独占地打开文件(即FileAccess.Write和FileShare.None)。 - Jakob Christensen
抱歉,我错了。我之前说的序列化在我的情况下并不完全准确。(继续) - George2
我的情况是,一个线程正在复制文件(使用File.Move方法),另一个线程正在从文件中读取。我不希望读取线程在复制过程中读取到文件的部分内容(这就是为什么我想要独占打开模式),我发布的代码解决了这个问题吗? - George2
是的,我认为您的代码解决了问题。在复制操作期间写入文件时,文件被锁定,因此您的代码将无法从文件中读取。 - Jakob Christensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接