I made a different experiment, because I was interested in the case of slow enumeration of the files, while more files are created inside the enumerated directory. The scenario of slow enumeration could happen for example if there is a SemaphoreSlim.WaitAsync
inside the enumeration loop (for throttling purposes). The experiment below starts by deleting all files from the target directory, then creating a specific number of initial files, and then starts enumerating the files with a 100 msec delay, while another asynchronous workflow creates more files at a rate of one file every 150 msec. Will the enumerator see the newly created files?
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}");
}
The answer is: it depends on the number of the initial files, and the length of the filenames. The threshold is at around 50 initial files, but it becomes smaller when the files have longer filenames. The enumeration will eventually stop, provided that the enumerator works faster than the files-producer, in which case a number of files will remain unobserved (typically around 20).
Here is the output of the above experiment for FILES_COUNT = 10
(meaning 10 existing files at the time the enumerator is created).
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 files are too few, so none of the files created afterwards were observed by the enumerator.