Qual é a maneira mais simples de bloquear um thread até que um arquivo seja desbloqueado e esteja acessível para leitura e renomeação? Por exemplo, existe um WaitOnFile () em algum lugar do .NET Framework?
Tenho um serviço que usa um FileSystemWatcher para procurar arquivos que devem ser transmitidos a um site FTP, mas o evento de arquivo criado dispara antes que o outro processo termine de gravar o arquivo.
A solução ideal teria um período de tempo limite para que o thread não ficasse para sempre antes de desistir.
Edit: Depois de experimentar algumas das soluções abaixo, acabei mudando o sistema para que todos os arquivos fossem gravados e Path.GetTempFileName()
, em seguida, executei um File.Move()
até o local final. Assim que o FileSystemWatcher
evento foi disparado, o arquivo já estava completo.
Respostas:
Esta foi a resposta que dei a uma pergunta relacionada :
fonte
Começando com a resposta de Eric, incluí algumas melhorias para tornar o código muito mais compacto e reutilizável. Espero que seja útil.
fonte
using
em um nulo, apenas verifique se há nulo dentro dousing
bloco.fs
não pode ser nula nocatch
bloco? Se oFileStream
construtor lançar, a variável não receberá um valor e não há mais nada dentro detry
que possa lançar umIOException
. Para mim, parece que não há problema em apenas fazerreturn new FileStream(...)
.Aqui está um código genérico para fazer isso, independente da própria operação de arquivo. Este é um exemplo de como usá-lo:
ou
Você também pode definir a contagem de novas tentativas e o tempo de espera entre as novas tentativas.
NOTA: Infelizmente, o erro Win32 subjacente (ERROR_SHARING_VIOLATION) não é exposto com .NET, então adicionei uma pequena função de hack (
IsSharingViolation
) baseada em mecanismos de reflexão para verificar isso.fonte
SharingViolationException
. Na verdade, eles ainda podem, de forma compatível com versões anteriores, contanto que desça deIOException
. E eles realmente deveriam.Starting with the .NET Framework 4.5, the HResult property's setter is protected, whereas its getter is public. In previous versions of the .NET Framework, both getter and setter are protected.
Eu organizei uma aula de auxiliar para esse tipo de coisa. Isso funcionará se você tiver controle sobre tudo o que acessa o arquivo. Se você está esperando contenção de um monte de outras coisas, então isso não vale a pena.
Ele funciona usando um mutex nomeado. Aqueles que desejam acessar o arquivo tentam adquirir o controle do mutex nomeado, que compartilha o nome do arquivo (com o '\' s transformado em '/' s). Você pode usar Open (), que irá travar até que o mutex esteja acessível ou você pode usar TryOpen (TimeSpan), que tenta adquirir o mutex para a duração fornecida e retorna false se não puder adquirir dentro do intervalo de tempo. Provavelmente, isso deve ser usado dentro de um bloco em uso, para garantir que os bloqueios sejam liberados corretamente e que o fluxo (se aberto) seja descartado corretamente quando este objeto for descartado.
Eu fiz um teste rápido com ~ 20 coisas para fazer várias leituras / gravações do arquivo e não vi corrupção. Obviamente não é muito avançado, mas deve funcionar para a maioria dos casos simples.
fonte
Para este aplicativo específico, observar diretamente o arquivo levará inevitavelmente a um bug difícil de rastrear, especialmente quando o tamanho do arquivo aumenta. Aqui estão duas estratégias diferentes que funcionarão.
Boa sorte!
fonte
Uma das técnicas que usei algum tempo atrás foi escrever minha própria função. Basicamente, capture a exceção e tente novamente usando um cronômetro que pode ser disparado por um determinado período. Se houver uma maneira melhor, por favor, compartilhe.
fonte
Do MSDN :
Seu FileSystemWatcher poderia ser modificado para não fazer sua leitura / renomeação durante o evento "OnCreated", mas sim:
fonte
Na maioria dos casos, uma abordagem simples como @harpo sugerida funcionará. Você pode desenvolver um código mais sofisticado usando esta abordagem:
fonte
Anúncio para transferir o arquivo de gatilho do processo SameNameASTrasferedFile.trg que é criado após a conclusão da transmissão do arquivo.
Em seguida, configure o FileSystemWatcher que irá disparar o evento apenas no arquivo * .trg.
fonte
Não sei o que você está usando para determinar o status de bloqueio do arquivo, mas algo como isso deve servir.
fonte
Uma possível solução seria, combinar um observador de sistema de arquivos com algumas pesquisas,
seja notificado para cada alteração em um arquivo e, ao ser notificado, verifique se ele está bloqueado conforme declarado na resposta atualmente aceita: https://stackoverflow.com/a/50800/6754146 O código para abrir o fluxo de arquivos é copiado da resposta e ligeiramente modificado:
Desta forma, você pode verificar se um arquivo está bloqueado e ser notificado quando for fechado durante o retorno de chamada especificado, desta forma você evita a pesquisa excessivamente agressiva e só faz o trabalho quando ele pode estar realmente fechado
fonte
Eu faço isso da mesma forma que Gulzar, apenas continue tentando com um loop.
Na verdade, nem me preocupo com o observador do sistema de arquivos. Pesquisar novos arquivos em uma unidade de rede uma vez por minuto é barato.
fonte
Basta usar o evento Changed com o NotifyFilter NotifyFilters.LastWrite :
fonte
Eu tive um problema semelhante ao adicionar um anexo do Outlook. "Usando" salvou o dia.
fonte
Que tal isso como uma opção:
Claro, se o tamanho do arquivo for pré-alocado na criação, você obterá um falso positivo.
fonte