并发File.Move是同一个文件

有人明确指出File.Move是primefaces操作:File.Move的primefaces性 。

但是,以下代码段会导致多次移动同一文件的可见性

有谁知道这段代码有什么问题?

using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace FileMoveTest { class Program { static void Main(string[] args) { string path = "test/" + Guid.NewGuid().ToString(); CreateFile(path, new string('a', 10 * 1024 * 1024)); var tasks = new List(); for (int i = 0; i  { try { string newPath = path + "." + Guid.NewGuid(); File.Move(path, newPath); // this line does NOT solve the issue if (File.Exists(newPath)) Console.WriteLine(string.Format("Moved {0} -> {1}", path, newPath)); } catch (Exception e) { Console.WriteLine(string.Format(" {0}: {1}", e.GetType(), e.Message)); } }); tasks.Add(task); } Task.WaitAll(tasks.ToArray()); } static void CreateFile(string path, string content) { string dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } using (FileStream f = new FileStream(path, FileMode.OpenOrCreate)) { using (StreamWriter w = new StreamWriter(f)) { w.Write(content); } } } } } 

矛盾的产出如下。 似乎该文件已多次移动到不同位置。 在磁盘上只有其中一个存在。 有什么想法吗?

 移动测试/ eb85560d-8c13-41c1-926a-6871be030742  - > test / eb85560d-8c13-41c1-926a-6871be030742.0018d317-ed7c-4732-92ac-3bb974d29017
移动测试/ eb85560d-8c13-41c1-926a-6871be030742  - > test / eb85560d-8c13-41c1-926a-6871be030742.3965dc15-7ef9-4f36-bdb7-94a5939b17db
移动测试/ eb85560d-8c13-41c1-926a-6871be030742  - > test / eb85560d-8c13-41c1-926a-6871be030742.fb66306a-5a13-4f26-ade2-acff3fb896be
移动测试/ eb85560d-8c13-41c1-926a-6871be030742  - > test / eb85560d-8c13-41c1-926a-6871be030742.c6de8827-aa46-48c1-b036-ad4bf79eb8a9
 System.IO.FileNotFoundException:找不到文件'C:\ file-move-test \ test \ eb85560d-8c13-41c1-926a-6871be030742'。
 System.IO.FileNotFoundException:找不到文件'C:\ file-move-test \ test \ eb85560d-8c13-41c1-926a-6871be030742'。
 System.IO.FileNotFoundException:找不到文件'C:\ file-move-test \ test \ eb85560d-8c13-41c1-926a-6871be030742'。
 System.IO.FileNotFoundException:找不到文件'C:\ file-move-test \ test \ eb85560d-8c13-41c1-926a-6871be030742'。
 System.IO.FileNotFoundException:找不到文件'C:\ file-move-test \ test \ eb85560d-8c13-41c1-926a-6871be030742'。
 System.IO.FileNotFoundException:找不到文件'C:\ file-move-test \ test \ eb85560d-8c13-41c1-926a-6871be030742'。 

生成的文件在这里:

  eb85560d-8c13-41c1-926a-6871be030742.fb66306a-5a13-4f26-ADE2,acff3fb896be 

UPDATE。 我可以确认检查File.Exists也没有解决问题 – 它可以报告单个文件真的被移动到几个不同的位置。

解决方案 我最终得到的解决方案如下:在使用源文件操作之前创建特殊的“锁定”文件,如果成功,那么我们可以确定只有这个线程获得对文件的独占访问权限,我们可以安全地做任何我们想要的事情。 下面是创建suck“lock”文件的正确参数集。

 File.Open(lockPath, FileMode.CreateNew, FileAccess.Write); 

有谁知道这段代码有什么问题?

我想这取决于你所说的“错误”。

您所看到的行为并非恕我直言,至少如果您正在使用NTFS(其他文件系统可能会或可能不会表现相似)。

底层OS API( MoveFile()和MoveFileEx()函数)的文档并不具体,但通常API是线程安全的,因为它们保证文件系统不会被并发操作损坏(当然,你的自己的数据可能已损坏,但它将以文件系统一致的方式完成。

最有可能发生的事情是,当移动文件操作继续进行时,它首先从给定目录链接获取实际文件句柄(在NTFS中,您看到的所有“文件名”实际上都是硬链接到底层文件对象)。 获得该文件句柄后,API然后为底层文件对象创建一个新文件名(即作为硬链接),然后删除以前的硬链接。

当然,随着这一进展,在获得底层文件句柄但在删除原始硬链接之前的时间段之间存在一个窗口。 这允许一些但不是所有其他并发移动操作看起来成功。 即最终原始硬链接不存在,进一步尝试移动它将不会成功。

毫无疑问,上述内容过于简单化了。 文件系统行为可能很复杂。 特别是,您所陈述的观察结果是,只有在完成所有操作后才能使用该文件的单个实例。 这表明API也以某种方式协调各种操作,这样只有一个新创建的硬链接幸存,可能是因为API实际上只是在检索文件对象句柄后重命名相关的硬链接,而不是创建一个新的并删除旧的(实现细节)。

在一天结束时,代码的“错误”在于它故意尝试对单个文件执行并发操作。 虽然文件系统本身将确保它保持连贯,但是由您自己的代码来确保这些操作是协调的,以便结果是可预测和可靠的。