为什么File.Move允许2个线程同时移动同一个文件?

him*_*msy 10 c# file-io multithreading

我们目前有一个应用程序监视新文件的文件夹.为了使其具有容错能力并能够一次处理更多文件,我们希望能够在不同的计算机上运行此应用程序的多个实例.我们使用File.Move"锁定"文件并确保一次只有一个线程可以处理文件.

为了测试只有一个应用程序和/或线程可以File.Move对文件执行,我创建了一个简单的应用程序(基于原始应用程序的代码),每个应用程序创建10个线程并监视文件夹,当每个线程检测到一个新文件时,它执行File.Move它并更改文件的扩展名,以尝试阻止其他线程执行相同操作.

我在运行此应用程序的多个副本时遇到了一个问题(并且它自己运行),其中2个线程(在同一个应用程序中或不同的应用程序中)都成功执行File.Move,没有异常抛出,但最后执行它的线程(我更改了文件的扩展名以包含DateTime.Now.ToFileTime()),成功重命名了该文件.我已经查看了File.Move它在执行操作之前检查文件是否存在,然后调用Win32Native.MoveFile以执行移动.

正如我所料,所有其他线程/应用程序都会抛出异常.

这是一个问题的原因是:

  1. 我以为只有一个线程可以一次执行File.Move一个文件.
  2. 我需要可靠地只有一个应用程序/线程能够一次处理一个文件.

以下是执行以下操作的代码File.Move:

public bool TryLock(string originalFile, out string changedFileName)
{
    FileInfo fileInfo = new FileInfo(originalFile);
    changedFileName = Path.ChangeExtension(originalFile, ".original." + DateTime.Now.ToFileTime());
    try
    {
        File.Move(originalFile, changedFileName);
    }
    catch (IOException ex)
    {
        Console.WriteLine("{3} - Thread {1}-{2} File {0} is already in use", fileInfo.Name, Thread.CurrentThread.ManagedThreadId, id, DateTime.Now.ToLongTimeString());
        return false;
    }
    catch (Exception ex)
    {
        Console.WriteLine("{3} - Thread {1}-{2} File {0} error {4}", fileInfo.Name, Thread.CurrentThread.ManagedThreadId, id, DateTime.Now.ToLongTimeString(), ex);
        return false;
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

注 - id只是我分配给每个线程进行日志记录的序号.

我在带有NTFS的SSD上运行Windows 7 Enterprise SP1.

him*_*msy 1

根据@marceln和@YuvalItzchakov的回答/评论,我尝试了以下方法,这似乎给出了更可靠的结果:

using (var readFileStream = File.Open(originalFile, FileMode.Open, FileAccess.Read, FileShare.Delete))
{
    readFileStream.Lock(0, readFileStream.Length - 1);
    File.Move(originalFile, changedFileName);
    readFileStream.Unlock(0, readFileStream.Length - 1);
}
Run Code Online (Sandbox Code Playgroud)

我想使用 Windows 自己的文件复制,因为它应该比复制流更有效,并且在生产中我们会将文件从一个网络共享移动到另一个网络共享。