Obtendo comprimento de arquivo atual / FileInfo.Length cache e informações obsoletas

Eu estou mantendo o controle de uma pasta de arquivos e seus comprimentos de arquivo, pelo menos um desses arquivos ainda está sendo gravado.

Eu tenho que manter um registro continuamente atualizado de cada tamanho de arquivo que eu uso para outros fins.

O método Update é chamado a cada 15 segundos e atualiza as propriedades do arquivo se o comprimento do arquivo for diferente do comprimento determinado na atualização anterior.

O método de atualização é algo como isto:

 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 } } 

Estou ciente do fato de que DirectoryInfo.GetFiles() é preencher previamente muitas das propriedades FileInfo , incluindo Length – e isso é ok, desde que nenhum cache seja feito entre as atualizações (informações armazenadas em cache não devem ter mais de 15 segundos).

Eu estava sob a suposição de que cada chamada DirectoryInfo.GetFiles() gera um novo conjunto de FileInfos que todos são preenchidos com informações novas, em seguida, usando a API Win32 FindFirstFile / FindNextFile . Mas isso não parece ser o caso.

Muito raramente, mas com certeza me deparo com situações em que o tamanho do arquivo que está sendo gravado não é atualizado por 5, 10 ou até 20 minutos por vez (o teste é feito no Windows 2008 Server x64, se isso for importante) .

Uma solução atual é chamar fi.Refresh() para forçar uma atualização em cada informação de arquivo. Isso internamente parece delegar a uma chamada de API do Win32 GetFileAttributesEx para atualizar as informações do arquivo.

Embora o custo de forçar uma atualização manualmente seja tolerável, eu prefiro entender por que estou obtendo informações obsoletas em primeiro lugar. Quando as informações de FileInfo geradas e como elas se relacionam à chamada de DirectoryInfo.GetFiles() ? Existe uma camada de cache de E / S de arquivo abaixo que não compreendo totalmente?

Raymond Chen escreveu agora uma postagem muito detalhada sobre exatamente esse problema:

Por que o tamanho do arquivo é relatado incorretamente para arquivos que ainda estão sendo gravados?

Em NTFS, os metadados do sistema de arquivos são uma propriedade não da input de diretório, mas sim do arquivo, com alguns dos metadados replicados na input do diretório como um ajuste para melhorar o desempenho da enumeração de diretório . Funções como FindFirstFile relatam a input de diretório e, colocando os metadados que os usuários FAT estavam acostumados a obter “de graça”, poderiam evitar ser mais lentos do que FAT para listagens de diretório. As funções de enumeração de diretório relatam os metadados atualizados pela última vez, que podem não corresponder aos metadados reais se a input de diretório for obsoleta.

Essencialmente, o desempenho é baixo: As informações de diretório reunidas em DirectoryInfo.GetFiles() e a API Win32 FindFirstFile / FindNextFile abaixo são armazenadas em cache por motivos de desempenho para garantir melhor desempenho em NTFS do que no antigo FAT para obter informações de diretório. Informações precisas sobre tamanho de arquivo só podem ser GetFileSize() chamando GetFileSize() em um arquivo diretamente (no .NET chame Refresh() no FileInfo ou adquira um FileInfo diretamente do nome do arquivo) – ou abrindo e fechando o stream de arquivo que causa a atualização informações do arquivo a serem propagadas para o cache de metadados do diretório. O último caso explica por que o tamanho do arquivo é atualizado imediatamente quando o processo de gravação fecha o arquivo.

Isso também explica que o problema aparentemente não apareceu no Windows 2003 Server – na época, as informações do arquivo eram replicadas com mais freqüência / sempre que o cache era liberado – esse não é mais o caso do Windows 2008 Server:

Quanto à freqüência, a resposta é um pouco mais complicada. A partir do Windows Vista (e sua correspondente versão do Windows Server que eu não sei, mas tenho certeza que você pode procurar, e por “você” quero dizer “Yuhong Bao”), o sistema de arquivos NTFS executa essa replicação de cortesia quando o último identificador para um object de arquivo é fechado. Versões anteriores do NTFS replicavam os dados enquanto o arquivo era aberto sempre que o cache era esvaziado, o que significava que ocorriam de tempos em tempos, de acordo com uma programação imprevisível. O resultado dessa alteração é que a input de diretório agora é atualizada com menos frequência e, portanto, o tamanho do arquivo atualizado pela última vez é mais desatualizado do que já era.

Ler o artigo completo é muito informativo e recomendado!

Eu thik que você deve usar o FileSystemWatcher e inscrever-se evento Changed. É acionado quando o item do sistema de arquivos especificado é alterado.

Eu concordo com o Wojteq que usar a class FileSystemWatcher seria uma solução melhor. Ele expõe events para quando diferentes atributos de um arquivo ou diretório são alterados (como o evento Change que ele referenciou) e é uma solução melhor do que a solução de pesquisa atualmente em vigor. Para responder à sua pergunta sobre por que a Atualização leva um tempo variável para refletir uma alteração no tamanho de um arquivo, a resposta é que isso tem a ver com o Gerenciador de Memória Virtual subjacente do sistema operacional Windows. Quando o File I / O é executado, ele atualiza os arquivos mapeados na memory; Esta é uma cópia em buffer do arquivo gerenciado pelo sistema operacional. Portanto, o Windows controla quando os dados em buffer são gravados no disco. Não há como prever quando uma determinada parte dos dados armazenados em buffer será gravada fisicamente no disco. Isso significa que a atualização de um stream de arquivos colocará essas atualizações no buffer. Se você fosse para Flush () o stream as atualizações em buffer devem ser gravadas no disco imediatamente, se você fechar o stream, ele será gravado do buffer para o disco logo após o stream ser fechado, e se o stream for mantido aberto, ele será ativado para o Windows quando ele decide gravar os dados em buffer no disco.