如果目录内容在迭代期间发生更改,Directory.EnumerateFiles会发生什么?

时间:2015-04-10 07:31:34

标签: file enumerate getfiles system.io.directory

我已经阅读过关于Directory.EnumerateFiles和Directory.GetFiles()之间差异的讨论。

据我所知,他们都在内部使用 System.IO.FileSystemEnumerableFactory.CreateFileNameIterator()

区别在于EnumerateFiles可能使用延迟执行(lazy),而GetFiles()执行ToArray,因此该函数已经执行。

但是如果在迭代期间将文件和文件夹添加到字典中会发生什么。迭代是否只会迭代EnumerateFiles()中出现的项目?

更糟糕的是:如果在迭代期间删除文件会发生什么:它们是否仍会被迭代?

3 个答案:

答案 0 :(得分:4)

感谢Michal Komorowski。然而,当我自己尝试他的解决方案时,我看到Directory.EnumerateFiles和Directory.GetFiles()之间的显着区别:

Directory.CreateDirectory(@"c:\MyTest");
// Create fies: b c e
File.CreateText(@"c:\MyTest\b.txt").Dispose();
File.CreateText(@"c:\MyTest\c.txt").Dispose();
File.CreateText(@"c:\MyTest\e.txt").Dispose();

string[] files = Directory.GetFiles(@"c:\MyTest");
var fileEnumerator = Directory.EnumerateFiles(@"c:\MyTest");

// delete file c; create file a d f
File.Delete(@"c:\MyTest\c.txt");
File.CreateText(@"c:\MyTest\a.txt").Dispose();
File.CreateText(@"c:\MyTest\d.txt").Dispose();
File.CreateText(@"c:\MyTest\f.txt").Dispose();

Console.WriteLine("Result from Directory.GetFiles");
foreach (var file in files) Console.WriteLine(file);
Console.WriteLine("Result from Directory.EnumerateFiles");
foreach (var file in fileEnumerator) Console.WriteLine(file);

这会产生不同的输出。

Result from Directory.GetFiles
c:\MyTest\b.txt
c:\MyTest\c.txt
c:\MyTest\e.txt
Result from Directory.EnumerateFiles
c:\MyTest\b.txt
c:\MyTest\d.txt
c:\MyTest\e.txt
c:\MyTest\f.txt

<强>结果:

  • GetFiles仍然会看到旧文件:B C E符合预期
  • EnumerateFiles查看了新文件D和F.它正确地跳过了已删除的文件C,但它错过了新文件A.

因此,EnumerateFiles和GetFiles之间的使用差异不仅仅是性能。

  • GetFiles会在您调用该函数时返回文件夹中的文件。这是可以预期的,因为它只是对字符串集合的枚举
  • EnumerateFiles正确跳过已删除的文件,但未看到所有添加的文件。如果文件夹更改而枚举结果是相当未定义的。

因此,如果您希望在枚举时更改文件夹,请仔细选择所需的功能

  • 期待GetFiles查看已删除的文件
  • 期望EnumerateFiles错过一些新文件。

答案 1 :(得分:0)

只有一种方法可以检查:

Directory.CreateDirectory(@"c:\\Temp");
File.Create(@"c:\\Temp\\a.txt").Close();
File.Create(@"c:\\Temp\\b.txt").Close();
File.Create(@"c:\\Temp\\c.txt").Close();
foreach (var f in Directory.EnumerateFiles(@"c:\\Temp"))
{
    Console.WriteLine(f);
    //Let's delete a file
    File.Delete(@"c:\\Temp\\c.txt");
    //Let's create a new file
    File.Create(@"c:\\Temp\\d.txt").Close();
}

最初 C:\ Temp 包含3个文件:a.txt,b.txt和c.txt。在迭代期间,正在删除其中一个文件,并且正在创建一个文件。最后, C:\ Temp 包含以下文件:a.txt,b.txt和d.txt但是,在控制台中,您将看到此目录的原始内容,即:。

c:\\Temp\a.txt
c:\\Temp\b.txt
c:\\Temp\c.txt

答案 2 :(得分:0)

我做了一个不同的实验,因为我对文件枚举缓慢的情况感兴趣,而在枚举目录中创建了更多文件。例如,如果枚举循环中有一个SemaphoreSlim.WaitAsync(用于节流目的),则可能发生缓慢枚举的情况。下面的实验首先从目标目录中删除所有文件,然后创建特定数量的初始文件,然后以100毫秒的延迟开始枚举文件,而另一个异步工作流以每150毫秒一个文件的速率创建更多文件。 。枚举器会看到新创建的文件吗?

static async Task Main(string[] args)
{
    const string FOLDER_PATH = @"C:\DirectoryEnumerateFilesTest";
    const int FILES_COUNT = 10;
    Console.WriteLine($"Deleting files");
    DeleteAllFiles(FOLDER_PATH);
    Console.WriteLine($"Creating files");
    await CreateFiles(FOLDER_PATH, startIndex: 1, filesCount: FILES_COUNT, delay: 0);
    Console.WriteLine($"Enumerating files while creating more files");
    var filePaths = Directory.EnumerateFiles(FOLDER_PATH);
    var cts = new CancellationTokenSource();
    var producer = CreateFiles(FOLDER_PATH,
        startIndex: 501, filesCount: 100, delay: 150, cts.Token);
    var enumeratedCount = 0;
    foreach (var filePath in filePaths)
    {
        Console.WriteLine($"Enumerated:   {Path.GetFileName(filePath)}");
        await Task.Delay(100);
        enumeratedCount++;
    }
    Console.WriteLine($"Total files enumerated: {enumeratedCount:#,0}");
    cts.Cancel();
    await producer;
}

private static void DeleteAllFiles(string folderPath)
{
    int count = 0;
    foreach (var filePath in Directory.GetFiles(folderPath))
    {
        File.Delete(filePath);
        Console.WriteLine($"File deleted: {Path.GetFileName(filePath)}");
        count++;
    }
    Console.WriteLine($"Total files deleted: {count:#,0}");
}

private static async Task CreateFiles(string folderPath,
    int startIndex, int filesCount, int delay, CancellationToken token = default)
{
    int count = 0;
    foreach (var i in Enumerable.Range(startIndex, filesCount))
    {
        var delayTask = Task.Delay(delay, token);
        await Task.WhenAny(delayTask);
        if (delayTask.IsCanceled) break;
        var fileName = $"File-{i:000}.txt";
        var filePath = Path.Combine(folderPath, fileName);
        File.WriteAllText(filePath, "Content");
        count++;
        Console.WriteLine($"File created: {fileName}");
    }
    Console.WriteLine($"Total files created: {count:#,0}");
}

答案是::这取决于初始文件的数量和文件名的长度。阈值大约为大约50个初始文件,但是当文件名较长时,阈值将变小。只要枚举器比文件生成器更快地运行,枚举将最终停止,在这种情况下,许多文件将保持不可见状态(通常为20个左右)。

以下是上述FILES_COUNT = 10实验的输出(表示创建枚举器时已有10个文件)。

Deleting files
Total files deleted: 0
Creating files
File created: File-001.txt
File created: File-002.txt
File created: File-003.txt
File created: File-004.txt
File created: File-005.txt
File created: File-006.txt
File created: File-007.txt
File created: File-008.txt
File created: File-009.txt
File created: File-010.txt
Total files created: 10
Enumerating files while creating more files
Enumerated:   File-001.txt
Enumerated:   File-002.txt
File created: File-501.txt
Enumerated:   File-003.txt
File created: File-502.txt
Enumerated:   File-004.txt
Enumerated:   File-005.txt
File created: File-503.txt
Enumerated:   File-006.txt
File created: File-504.txt
Enumerated:   File-007.txt
Enumerated:   File-008.txt
File created: File-505.txt
Enumerated:   File-009.txt
File created: File-506.txt
Enumerated:   File-010.txt
Total files enumerated: 10
File created: File-507.txt
Total files created: 7

10个文件太少,因此枚举器没有观察到此后创建的文件。