получение текущей длины файла / FileInfo. Длина кэширования и устаревшая информация - PullRequest
17 голосов
/ 20 октября 2011

Я отслеживаю папку с файлами и их длину, по крайней мере, один из этих файлов все еще записывается.

Я должен постоянно обновлять записи о длине каждого файла, который я используюдля других целей.

Метод Update вызывается каждые 15 секунд и обновляет свойства файла, если длина файла отличается от длины, определенной в предыдущем обновлении.

Метод обновления выглядит как-токак это:

var directoryInfo = new DirectoryInfo(archiveFolder);
var archiveFiles = directoryInfo.GetFiles()
                                .OrderByDescending(f=>f.CreationTimeUtc); 
foreach (FileInfo fi in archiveFiles)
{
    //check if file existed in previous update already
    var origFileProps = cachedFiles.GetFileByName(fi.FullName);
    if (origFileProps != null && fi.Length == origFileProps.EndOffset)
    {
        //file length is unchanged
    }
    else
    {
        //Update the properties of this file
        //set EndOffset of the file to current file length
    }
}

Я знаю, что DirectoryInfo.GetFiles() предварительно заполняет многие из FileInfo свойств, включая Length - и это нормально, покапоскольку кэширование не выполняется между обновлениями (кэшированная информация не должна быть старше 15 секунд).

Я предполагал, что каждый вызов DirectoryInfo.GetFiles() генерирует new набор FileInfos, которые все заполняются свежей информацией прямо тогда с использованием FindFirstFile / FindNextFile Win32 API.Но, похоже, это не так.

Очень редко, но, в конце концов, я наверняка столкнусь с ситуациями, когда длина файла для записываемого файла не обновляется в течение 5, 10 или даже 20 минут.за раз (тестирование выполняется на Windows 2008 Server x64, если это имеет значение).

Текущий обходной путь - вызвать fi.Refresh(), чтобы принудительно обновить информацию о каждом файле.Кажется, это внутренне делегирует вызов GetFileAttributesEx Win32 API для обновления информации о файле.

Хотя стоимость принудительного обновления вручную терпима, я бы лучше понял почему Я получаю устаревшиеинформация в первую очередь.Когда генерируется информация FileInfo и как она связана с вызовом DirectoryInfo.GetFiles()?Есть ли слой кэширования файлового ввода-вывода, который я не до конца понимаю?

Ответы [ 3 ]

15 голосов
/ 28 декабря 2011

Раймонд Чен (Raymond Chen) написал очень подробную запись в блоге именно об этой проблеме:

Почему размер файла сообщается неправильно для файлов, в которые все еще выполняется запись?

В NTFS метаданные файловой системы являются свойством не записи каталога, а файла, причем некоторые метаданные реплицируются в запись каталога в качестве настройки для улучшения каталогаперечисление производительности .Такие функции, как FindFirstFile, сообщают о записи каталога, и, помещая метаданные, которые пользователи FAT привыкли получать «бесплатно», они могли бы избежать медленнее, чем FAT для списков каталогов. Функции перечисления каталогов сообщают о последних обновленных метаданных, которые могут не соответствовать фактическим метаданным, если запись каталога устарела.

По сути, это сводится к производительности:информация о каталогах, полученная из DirectoryInfo.GetFiles() и FindFirstFile / FindNextFile Win32 API ниже, кэшируется по соображениям производительности, чтобы гарантировать лучшую производительность в NTFS, чем в старой FAT для получения информации о каталогах.Точную информацию о размере файла можно получить только путем непосредственного вызова Get­File­Size() для файла (в .NET вызов Refresh() для FileInfo или получения FileInfo непосредственно из имени файла) - или открытия и закрытия потока файловчто приводит к распространению обновленной информации о файле в кэш метаданных каталога.В последнем случае объясняется, почему размер файла немедленно обновляется, когда процесс записи закрывает файл.

Это также объясняет, что проблема, по-видимому, не обнаруживалась в Windows 2003 Server - тогда информация о файле копировалась чаще/ всякий раз, когда кэш был очищен - это больше не относится к Windows 2008 Server:

Что касается того, как часто, ответ немного сложнее.Начиная с Windows Vista (и соответствующей ей версии Windows Server, которую я не знаю, но я уверен, что вы можете найти ее, а под словом «вы» я имею в виду «Yuhong Bao»), файловая система NTFS выполняет эту реплику вежливости, когдапоследний дескриптор объекта файла закрыт. Более ранние версии NTFS реплицировали данные, когда файл был открыт всякий раз, когда кэш очищался, что означало, что это происходило очень часто по непредсказуемому графику.Результатом этого изменения является то, что запись каталога теперь обновляется реже, и, следовательно, размер последнего обновленного файла является более устаревшим, чем он был.

ЧтениеПолная статья очень информативна и рекомендуется!

5 голосов
/ 20 октября 2011

Мне кажется, что вы должны использовать FileSystemWatcher и подписаться на измененное событие.Он срабатывает при изменении указанного элемента файловой системы.

1 голос
/ 20 октября 2011

Я согласен с Wojteq, что использование класса FileSystemWatcher было бы лучшим решением. Он предоставляет события, когда изменяются различные атрибуты файла или каталога (например, событие Change, на которое он ссылался), и это лучшее решение, чем имеющееся в настоящее время решение для опроса. Чтобы ответить на ваш вопрос о том, почему для обновления требуется различное количество времени, чтобы отразить изменение размера файла, ответ заключается в том, что он связан с базовым диспетчером виртуальной памяти операционной системы Windows. Когда выполняется File I / O, он фактически обновляет файлы, отображенные в памяти; это буферизованная копия файла, который управляется операционной системой. Итак, Windows контролирует, когда буферизованные данные записываются на диск. Невозможно предсказать, когда конкретный фрагмент буферизованных данных будет физически записан на диск. Это означает, что обновление потока файлов поместит эти обновления в буфер. Если вы выполняете Flush () поток, буферизованные обновления должны быть немедленно записаны на диск, если вы закроете поток, то он будет записан из буфера на диск сразу после закрытия потока, и если поток остается открытым, он работает в Windows, когда он решает записать буферизованные данные на диск.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...