Una guida pratica a FileSystemWatcher: gestire gli eventi persi e duplicati
· Aggiornato il: · Go Komura · FileSystemWatcher, C#, .NET, Sviluppo Windows, Integrazione file, Progettazione
FileSystemWatcher è la prima API che viene visualizzata durante il monitoraggio delle modifiche ai file in .NET su Windows. Fornisce comodamente creazioni, modifiche, eliminazioni e ridenominazioni di file e directory come eventi, ma se tratti Created o Changed come notifiche di completamento, verrai regolarmente bruciato da eventi persi, notifiche duplicate e lettura di file scritti a metà.
In questo articolo, organizziamo come utilizzare FileSystemWatcher e le sue insidie, presupponendo un’integrazione principalmente basata su file con .NET su Windows. Per i concetti sottostanti di mutua esclusione, vedere anche Fondamenti di esclusione reciproca per l’integrazione basata su file: best practice per i blocchi dei file e le rivendicazioni atomiche.
In realtà, Created può attivarsi mentre un file è ancora in fase di copia e non è assolutamente garantito che Changed si attivi solo una volta. Quando le modifiche si concentrano in una finestra breve, il buffer interno può traboccare e le singole modifiche vengono eliminate.
Quindi il nocciolo del progetto è questo.
- Le notifiche sono trigger
- La verità risiede nella nuova scansione della directory
- La proprietà deriva da una rivendicazione atomica
- L’idempotenza cattura ciò che resta
Nel corpo dell’articolo, esaminiamo le trappole del cablaggio di FileSystemWatcher nell’integrazione dei file con questa mentalità.
Il codice in questo articolo è pubblicato in GitHub come set di esempi completo compilabile ed eseguibile (una libreria, una demo della console eseguita su una directory temporanea e unit test che creano e modificano effettivamente i file per verificare gli eventi).
filesystemwatcher-safe-basics - komurasoft-blog-samples (GitHub)
Sommario
- Prima la conclusione (in una riga)
- 1.1. Un esempio minimo che funziona
- Modelli di malintesi comuni con
FileSystemWatcher(diagrammi)- 2.1. Trattare
Createdcome una notifica di completamento - 2.2. Fidarsi del Conte e dell’Ordine di
Changed - 2.3. Perdere le modifiche a causa dell’overflow del buffer interno
- 2.1. Trattare
- Anti-modelli
- 3.1. Elaborazione direttamente all’interno del gestore eventi
- 3.2. Tentativo di ricostruire il vero stato dal flusso di eventi
- 3.3. Trattare “No More
Changed” come Completamento - 3.4. Credere in un
InternalBufferSizepiù grande risolve tutto - 3.5. Registrare
Errore ignorarlo
- Migliori pratiche
- 4.1. Piega le notifiche in “Richieste di nuova scansione”
- 4.2. Rendi esplicito il completamento sul lato mittente
- 4.3. Il ricevente accetta un reclamo atomicamente
- 4.4. Nuova scansione completa all’avvio / overflow / riconnessione
- 4.5. Assumere idempotenza
- Pseudocodice (estratti)
- 5.1. Il tipico modello di fallimento
- 5.2. Un esempio nella giusta direzione (approssimativamente abbozzato)
- Una guida approssimativa alla scelta
- Conclusione
- Riferimenti
1. Prima la conclusione (in una riga)
- Gli eventi
FileSystemWatchernon sono notifiche di completamento: indicano che qualcosa è cambiato Created/Changed/Renamedpossono duplicarsi, arrivare in un ordine che non ti aspettavi e cadere in caso di overflow- I gestori eventi sono più stabili quando non svolgono lavori pesanti e accodano solo una richiesta di nuova scansione
- Il rilevamento del completamento deve essere reso esplicito tramite file / manifest
temp -> close -> rename / replaceodone - Con più lavoratori, è necessario presentare un reclamo atomicamente prima di leggere
- L’accordatura
InternalBufferSizeè un aiuto. Alla fine, le nuove scansioni complete e l’idempotenza sono ciò che funziona
In breve: non trattare FileSystemWatcher come “un flusso storico veritiero”.
Le cose si rompono molto meno se le notifiche non rimangono altro che un segnale di “è ora di andare a vedere”.
1.1. Un esempio minimo che funziona
Per chi non ha mai toccato FileSystemWatcher, ecco la forma minima che mostra solo il percorso felice. Dai prossimi capitoli in poi si parlerà delle trappole che nascono dal fatto che anche queste poche righe “funzionano”.
// C# / .NET 8 app console. Forma minima per verificare solo che arrivino notifiche
using System.IO;
using var watcher = new FileSystemWatcher(@"C:\incoming")
{
Filter = "*.csv",
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
};
watcher.Created += (_, e) => Console.WriteLine($"Created: {e.FullPath}");
watcher.Changed += (_, e) => Console.WriteLine($"Changed: {e.FullPath}");
watcher.Renamed += (_, e) => Console.WriteLine($"Renamed: {e.OldFullPath} -> {e.FullPath}");
watcher.Error += (_, e) => Console.WriteLine($"Error: {e.GetException().Message}");
watcher.EnableRaisingEvents = true; // da qui inizia la sorveglianza
Console.WriteLine("Premi Invio per uscire");
Console.ReadLine();
Anche in questa forma minima, tre punti aiutano a non perdere tempo.
- Fino a quando non si imposta
EnableRaisingEvents = true, non arriva nemmeno un evento. Aver registrato i gestori non basta - La durata del
watchercoincide con quella dell’app. Se esce dallo scope di una variabile locale e viene distrutto, le notifiche si fermano lì. Se deve rimanere attivo, tienilo in un campo o in un altro posto che sopravvive - Il valore predefinito di
NotifyFilterè la combinazioneLastWrite | FileName | DirectoryName(vedi FileSystemWatcher.NotifyFilter Property nei riferimenti). Scegliere esplicitamente cosa raccogliere aiuta a non lasciare il dubbio in seguito
Ed è importante ricordare che questo codice verifica solo “che gli eventi arrivino”. Non dice se possiamo leggere il file al momento del Created, né se le notifiche vadano perse. Da qui inizia il vero argomento.
2. Modelli di malintesi comuni con FileSystemWatcher (diagrammi)
2.1. Trattare Created come una notifica di completamento
Questa è la mina più evidente.
Durante le copie e i trasferimenti, Created si attiva nel momento in cui il file viene creato e successivamente possono seguire uno o più eventi Changed.
sequenceDiagram
participant Sender as Sender
participant Share as watched dir
participant W as FileSystemWatcher
participant Receiver as Receiver
Sender->>Share: Creates orders.csv
Share-->>W: Created
W-->>Receiver: OnCreated
Receiver->>Share: Opens and reads orders.csv
Note over Receiver: Copy still in progress
Sender->>Share: Writes the rest
Share-->>W: Changed
Share-->>W: Changed
Note over Receiver: Missing rows / corrupt JSON / corrupt ZIP
Created può significare “il nome è diventato visibile”, ma non garantisce che “ora sia sicuro da leggere”.
Se si equiparano i due, si entra nella stessa miniera della sezione 2.1 dell’articolo precedente, solo per un percorso diverso.
2.2. Fidarsi del Conte e dell’Ordine di Changed
Non è garantito che Changed spari esattamente una volta.
Anche le operazioni ordinarie come lo spostamento o il salvataggio possono essere visualizzate come più eventi. Oltre a ciò, puoi anche apprendere i dettagli dei software antivirus e degli indicizzatori.
sequenceDiagram
participant App as Saving app
participant Dir as watched dir
participant AV as AV / indexer
participant W as FileSystemWatcher
App->>Dir: Starts saving report.xlsx
Dir-->>W: Created
Dir-->>W: Changed
App->>Dir: Rename from a temp file
Dir-->>W: Renamed
Dir-->>W: Changed
AV->>Dir: Scan / attribute access
Dir-->>W: Changed
Note over W: Not guaranteed to be once-only or in this order
Aspettative come “uno Changed significa fatto” o “niente tocca il file dopo Renamed” sono piuttosto precarie.
Note aggiuntive:
- La ridenominazione di un file può produrre un evento
Changed RenamedEventArgs.Namepuò esserenullquando OS non può correlare i vecchi / nuovi nomi- I file nascosti non sono esenti. “È un nome temporaneo nascosto, quindi non sarà visibile” non è valido
- Se la directory controllata stessa viene rinominata, tale modifica non viene segnalata
2.3. Perdere le modifiche a causa dell’overflow del buffer interno
FileSystemWatcher ha un buffer interno.
Quando le modifiche si concentrano in una breve finestra, questa va in overflow e le singole notifiche vengono eliminate.
flowchart LR
A[Molte modifiche in una breve finestra] --> B[Le notifiche si accumulano nel buffer interno]
B --> C{L'elaborazione riesce a stare al passo?}
C -- "Sì" --> D[Elabora gli eventi individuali in ordine]
C -- "No" --> E[Overflow]
E --> F[Evento di errore]
F --> G[Non fidarti più della completezza della cronologia per evento]
G --> H[Nuova scansione completa della directory]
Il punto importante qui è che “un overflow significa perdere solo un evento” non è garantito. La completezza dell’intero flusso di eventi individuali diventa sospetta, quindi è meglio semplicemente riesaminare l’intero quadro.
3. Anti-modelli
3.1. Elaborazione direttamente all’interno del gestore eventi
Ciò attribuisce troppo peso – rilevamento del completamento e acquisizione della proprietà – agli eventi stessi.
watcher.Created += (_, e) =>
{
using var stream = File.OpenRead(e.FullPath);
Import(stream); // May still be mid-copy
};
watcher.Error += (_, e) =>
{
Console.WriteLine(e.GetException()); // Just printing it
};
Ci sono due problemi.
- Al momento del
Created, i contenuti potrebbero essere incompleti - Non è possibile ripristinare i dati in caso di errori o overflow
Un gestore eventi dà il meglio di sé quando solleva semplicemente una richiesta di nuova scansione e restituisce immediatamente il risultato. Se avvii pesanti aggiornamenti I / O o DB qui, ti strangoli durante i burst.
3.2. Tentativo di ricostruire il vero stato dal flusso di eventi
Il design di “aggiungi a un dizionario su Created, aggiorna su Changed, rimuovi su Deleted, reinserisci su Renamed” sembra pulito a prima vista.
Ma una volta che entrano duplicati, divisioni, overflow e interferenze esterne, la contabilità smette gradualmente di sommarsi.
switch (e.ChangeType)
{
case WatcherChangeTypes.Created:
state[e.FullPath] = Pending;
break;
case WatcherChangeTypes.Changed:
state[e.FullPath] = Modified;
break;
case WatcherChangeTypes.Deleted:
state.Remove(e.FullPath);
break;
}
Piuttosto che lottare in questa direzione, è più forte ricontrollare ogni volta i file effettivi sul disco. Ciò che conta nell’integrazione dei file è trovare correttamente ciò che può essere elaborato in questo momento, non ricostruire fedelmente la cronologia degli eventi.
3.3. Trattare “No More Changed” come completamento
Questo design ha lo stesso odore dell’articolo precedente “le dimensioni hanno smesso di cambiare, quindi è fatta”. Sembra conveniente, ma determina il completamento indovinando.
if (lastChangedAt + TimeSpan.FromSeconds(10) < DateTime.UtcNow)
{
return Ready;
}
I casi in cui ciò fallisce includono:
- Una copia di file di grandi dimensioni viene interrotta a metà
- L’app di invio salva in più fasi
- Le notifiche appaiono ritardate su una condivisione di rete
- Un processo esterno riscrive successivamente gli attributi o i timestamp
Il completamento è più stabile se dichiarato esplicitamente piuttosto che indovinato.
3.4. Credere in un InternalBufferSize più grande risolve tutto
L’accordatura InternalBufferSize è importante, ma non è il cuore del design.
- Il valore predefinito è
8192byte - Non può scendere al di sotto di
4096byte e non può superare64 KB - Il buffer utilizza memoria non paginata, quindi aumentarlo non è così casuale come sembra
In altre parole, anche al 64 KB, una notifica scoppiata oltre mette fine alla storia.
E non risolve il problema “è una notifica di completamento?” problema anche solo di un millimetro.
Prima di ampliare il buffer, ci sono alcune cose da fare.
- Restringi il campo dell’orologio con
Filter/Filters - Mantenere
NotifyFilteral minimo necessario - Non impostare
IncludeSubdirectoriessutruecon noncuranza - Mantenere leggeri i gestori di eventi
- Aggiungi nuove scansioni complete e idempotenza
3.5. Registrare Error e ignorarlo
Error non è il tipo di notifica che puoi “vedere occasionalmente e scrollarti di dosso”.
Overflow del buffer e problemi nel continuare a osservare emergono qui.
watcher.Error += (_, e) =>
{
_logger.LogError(e.GetException(), "watcher error");
// Ending here means noticing the loss but never recovering
};
Come minimo, vuoi arrivare così lontano.
- Richiedi una nuova scansione completa
- Se hai dubbi sulla continuazione della visione, valuta la possibilità di ricreare l’osservatore
- Rendere la rielaborazione idempotente, partendo dal presupposto che gli eventi siano andati perduti
4. Migliori pratiche
4.1. Piega le notifiche in “Richieste di nuova scansione”
Il cablaggio di Created / Changed / Deleted / Renamed / Error ciascuno direttamente in una logica aziendale separata rovina la chiarezza. Per prima cosa riuniscili tutti in un unico tipo di segnale: “vai a vedere”.
flowchart LR
A[Created / Changed / Deleted / Renamed] --> Q[scan request]
B[Error / overflow] --> Q
C[startup] --> Q
Q --> D[Rescan the directory]
D --> E[Enumerate ready candidates]
E --> F[Attempt a claim]
Punti di implementazione:
- Nel gestore eventi, fai poco più che impostare
dirty = truee inviare un segnale - Concentrare la scansione in un unico operatore
- Durante i burst, coalesce per circa 100-300 ms, quindi esegue la scansione una volta
- Se durante una scansione arrivano più notifiche, esegui nuovamente la scansione in seguito
In questo modo, che arrivino cinque o cinquanta eventi, l’azione finale è unitaria: “guarda i dossier attuali e trova quello che è pronto”.
4.2. Rendi esplicito il completamento sul lato mittente
Se controlli anche il mittente, la correzione del protocollo di pubblicazione evita lo sforzo sul rilevamento del completamento sul lato FileSystemWatcher.
Il percorso provato, ancora una volta, è:
- Scrivi l’intero contenuto sotto un nome
temp closeessorename / replacesullo stesso file system- Se necessario, inserisci per ultimo un file / manifest
done
flowchart TD
A[Write the full content to data.tmp] --> B[flush / close]
B --> C[rename / replace to data.csv]
C --> D[Place data.done / manifest.json]
D --> E[Receiver watches only final names or done files]
Come nell’articolo precedente, ma è qui che sta il vero vantaggio.
Il modo giusto di vedere FileSystemWatcher non è come uno strumento che inventa il completamento, ma come uno strumento che nota prima il completamento dichiarato esplicitamente.
4.3. Il ricevente accetta un reclamo atomicamente
Anche quando una nuova scansione trova un candidato pronto, l’accesso diretto alla lettura consente a più operatori di acquisirlo contemporaneamente. Quindi accetta un reclamo atomicamente prima dell’elaborazione.
sequenceDiagram
participant Scan as scanner
participant IN as incoming
participant P1 as processing/worker1
participant P2 as processing/worker2
Scan->>IN: Finds order-123
Scan->>P1: rename order-123
Scan->>P2: rename order-123
Note over P1,P2: Only the one that succeeds first holds ownership
Come accennato nell’articolo precedente, la ridenominazione incoming -> processing/<worker>/ è la più chiara.
Raggruppare il payload + manifest + file ausiliari in un’unica directory è particolarmente conveniente, poiché puoi quindi richiedere il pacchetto.
incoming/
order-123/
payload.csv
manifest.json
Con questo, una singola ridenominazione della directory del bundle diventa proprietaria.
4.4. Nuova scansione completa all’avvio / overflow / riconnessione
Questo è abbastanza importante.
- I file posizionati prima dell’avvio dell’app non vengono rilevati dagli eventi
- Una volta che si verifica un overflow, diventa difficile fidarsi del singolo flusso di eventi
- Con condivisioni di rete e disconnessioni temporanee in gioco, è più lecito presumere che sia mancato “qualcosa nel mezzo”.
Quindi, almeno in questi momenti, dovrebbe essere eseguita una nuova scansione completa.
- All’avvio
- Alla ricezione di
Error - Subito dopo aver ricreato l’osservatore
- Periodicamente, a titolo assicurativo, a intervalli fissi
La filosofia qui: “l’osservatore è un accenno ai delta; la nuova scansione è il recupero della coerenza”.
4.5. Assumere idempotenza
Con FileSystemWatcher finirai per esaminare lo stesso target più volte.
Non si tratta di un bug: è più stabile accettarlo come parte del progetto.
Concretamente funziona così.
- Inserisci un
IdempotencyKeynel manifest - Se già elaborato, non rieseguire gli effetti collaterali
- Rendere verificabili gli stati archiviati / DB-registrati / inviati
- Assicurarsi che una nuova scansione completa non sia altro che “guardare di nuovo in sicurezza le stesse cose”
Cercare di costruire esattamente una volta solo a partire dagli eventi diventa rapidamente doloroso. Accettare almeno una volta e chiudere il ciclo con l’idempotenza è la posizione più forte nella pratica.
5. Pseudocodice (estratti)
5.1. Il tipico modello di fallimento
using var watcher = new FileSystemWatcher(incomingDir)
{
Filter = "*.csv",
IncludeSubdirectories = false,
EnableRaisingEvents = true,
InternalBufferSize = 64 * 1024
};
watcher.Created += (_, e) =>
{
// Assumes Created = completion notification
ProcessFile(e.FullPath);
};
watcher.Changed += (_, e) =>
{
// It keeps firing, so just process again
ProcessFile(e.FullPath);
};
watcher.Error += (_, e) =>
{
Console.WriteLine(e.GetException());
// No recovery
};
Ci sono quattro problemi.
Created/Changedsono collegati direttamente all’elaborazione aziendale- Non è previsto il rilevamento del completamento
- Nessuna nuova scansione completa in caso di overflow
- Nessun meccanismo per interrompere l’elaborazione ripetuta dello stesso file
5.2. Un esempio nella giusta direzione (approssimativamente abbozzato)
private readonly SemaphoreSlim _scanSignal = new(0, int.MaxValue);
private int _scanRequested = 0;
private int _fullRescanRequested = 0;
void OnAnyChange(object? sender, FileSystemEventArgs e)
{
RequestScan(full: false);
}
void OnRenamed(object? sender, RenamedEventArgs e)
{
RequestScan(full: false);
}
void OnError(object? sender, ErrorEventArgs e)
{
Log(e.GetException());
RequestScan(full: true);
}
void RequestScan(bool full)
{
if (full)
{
Interlocked.Exchange(ref _fullRescanRequested, 1);
}
if (Interlocked.Exchange(ref _scanRequested, 1) == 0)
{
_scanSignal.Release();
}
}
async Task ScannerLoopAsync(CancellationToken cancellationToken)
{
RequestScan(full: true); // startup scan
while (!cancellationToken.IsCancellationRequested)
{
await _scanSignal.WaitAsync(cancellationToken);
// Coalesce notification bursts a little
await Task.Delay(TimeSpan.FromMilliseconds(200), cancellationToken);
Interlocked.Exchange(ref _scanRequested, 0);
bool full = Interlocked.Exchange(ref _fullRescanRequested, 0) == 1;
foreach (var bundle in EnumerateReadyBundles(incomingDir, full))
{
var claimedPath = Path.Combine(processingDir, bundle.Name);
if (!TryClaimByRename(bundle.Path, claimedPath))
{
continue; // Another worker claimed it first
}
var manifest = ReadManifest(Path.Combine(claimedPath, "manifest.json"));
if (AlreadyProcessed(manifest.IdempotencyKey))
{
MoveToArchive(claimedPath, archiveDir);
continue;
}
ProcessBundle(claimedPath);
RecordProcessed(manifest.IdempotencyKey);
MoveToArchive(claimedPath, archiveDir);
}
if (Volatile.Read(ref _scanRequested) == 1)
{
_scanSignal.Release(); // Do not drop notifications that arrived mid-scan
}
}
}
Ciò che conta in questo esempio è il flusso, non i dettagli dell’API.
- Piega le notifiche in richieste di scansione
- Trova ciò che è pronto eseguendo la scansione
- Presenta un reclamo
- Controllare l’idempotenza
- Elabora, registra e sposta nell’archivio
Gli eventi FileSystemWatcher qui non sono altro che un trigger.
6. Una guida approssimativa alla scelta
-
Lavoratore ricevente singolo / puoi anche correggere il mittente Inizia con
temp -> close -> renamee una scansione all’avvio. Solo questo ti porta abbastanza lontano. -
Lavoratori riceventi multipli Aggiungi la ridenominazione della rivendicazione
incoming -> processingoltre a quanto sopra. -
Alta frequenza, ricco di notifiche Restringi
Filter/NotifyFilter/IncludeSubdirectoriese riduci al minimo i gestori di eventi. L’accordaturaInternalBufferSizeviene dopo. -
Gli overflow danneggiati / eventi persi non sono accettabili Basati su nuove scansioni complete e, se ciò non bastasse, non scommettere solo su
FileSystemWatcher. Se utilizzi solo Windows, anche il journal delle modifiche USN è un’opzione. -
Non puoi controllare come scrive l’altro sistema Piuttosto che nascondere le condizioni di completamento con ipotesi, considerare innanzitutto se il protocollo di pubblicazione può essere negoziato. In caso contrario, abbassare il livello di garanzia e appoggiarsi a un design di ricezione idempotente.
Gli ultimi due elementi sono criteri di prelievo abbastanza importanti.
FileSystemWatcher è utile, ma non è un rilevatore di verità onnipotente.
In cosa differisce il USN change journal
Il USN change journal è un registro delle modifiche mantenuto da NTFS a livello di volume. A differenza delle notifiche di directory come FileSystemWatcher, che se l’app non è in esecuzione al momento del cambiamento non vengono ricevute, il change journal conserva il record sul lato volume, quindi le modifiche avvenute mentre l’app era ferma possono essere rilette successivamente a partire dalla posizione (USN) letta in precedenza. Anche nella documentazione Microsoft, tra i punti deboli delle notifiche di directory è citata la necessità di tenere l’app sempre in esecuzione, e come soluzione viene proposto proprio il change journal.
D’altra parte, il carico aumenta.
FileSystemWatcher |
USN change journal | |
|---|---|---|
| Unità di monitoraggio | Directory specificata (+ sottodirectory) | Intero volume. L’app deve filtrare il range desiderato |
| Quando l’app è ferma | Non si sa. Si compensa con full rescan | Si possono rileggere i record |
| Perdite | Overflow del buffer interno | Oltre il limite del journal, i record vecchi vengono eliminati |
| Requisiti | Solo le API .NET | Handle del volume e chiamate FSCTL_*. Creazione / eliminazione del journal richiede privilegi di amministratore |
Quindi, è un’opzione da considerare quando i requisiti includono “non può essere sempre in esecuzione” o “voglio recuperare anche le modifiche avvenute durante l’arresto”. Se questi requisiti non esistono, FileSystemWatcher + full rescan è un’implementazione più semplice.
7. Conclusione
FileSystemWatcher non sostituisce le notifiche di completamento. La verità non è nel flusso degli eventi ma in ciò che è visibile sul disco in questo momento. Rendi esplicito il completamento tramite file / manifest temp -> close -> rename / replace o done e decidi la proprietà accettando una rivendicazione in modo atomico. È lì che vive il cuore del design.
Elaborare immediatamente il Created, fidarsi del conteggio o dell’ordine di Changed, considerare “niente più Changed” come completamento, consolarsi solo con InternalBufferSize, vedere Error e non riprendersi mai: tutti progetti da evitare. Invece, raggruppa le notifiche in richieste di nuova scansione, esegui nuove scansioni complete all’avvio / overflow / riconnessione, assumi la proprietà tramite la ridenominazione delle richieste e assorbi i duplicati e le nuove scansioni con idempotenza.
In altre parole, il trucco con FileSystemWatcher è non equiparare mai “l’aver ricevuto un evento” con “l’autorizzazione all’elaborazione”.
La semplice separazione di questi due riduce notevolmente il tipo di codice di monitoraggio che si interrompe solo una volta ogni tanto.
8. Riferimenti
- Codice di esempio completo per questo articolo (libreria, demo, test unitari) https://github.com/gomurin0428/komurasoft-blog-samples/tree/main/filesystemwatcher-safe-basics
- Articolo correlato: Fondamenti di esclusione reciproca per l’integrazione basata su file: best practice per i blocchi dei file e le attestazioni atomiche
- FileSystemWatcher Classe (System.IO)
- System.IO.FileSystemWatcher classe - .NET
- FileSystemWatcher.InternalBufferSize Immobile (System.IO)
- FileSystemWatcher.Error Evento (System.IO)
- FileSystemWatcher.Created Evento (System.IO)
- FileSystemWatcher.Changed Evento (System.IO)
- FileSystemWatcher.Renamed Evento (System.IO)
- Cambia diari - App Win32
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
Perché utilizzare .NET Generic Host e BackgroundService nelle app desktop
Come utilizzare Generic Host e BackgroundService per organizzare l'avvio, l'elaborazione periodica, l'arresto, la registrazione, la confi...
Icone nella system tray e notifiche toast nelle app Windows — le insidie di NotifyIcon e come scegliere l'AppNotification giusta
Una guida pratica per mantenere un'applicazione Windows aziendale residente nella system tray (area di notifica) e avvisare l'utente tram...
Fino a quando funzioneranno le applicazioni VB6? — Lo stato del supporto al runtime e un percorso pratico verso la migrazione a .NET
Fino a quando continueranno a funzionare le applicazioni VB6? Questo articolo chiarisce l'asimmetria tra la politica di supporto del runt...
Era giapponese, festività e date di chiusura nelle applicazioni aziendali — progettazione resiliente ai cambi di era, JapaneseCalendar e calcolo dei giorni lavorativi nella pratica
Mostrare «Reiwa 8» su un documento, calcolare i giorni lavorativi escludendo le festività, pagare entro la fine del mese successivo dopo ...
Pitfall delle app di comunicazione seriale - Riconnessione e progettazione dei log
I pitfall delle app di comunicazione seriale da evitare nell'integrazione dispositivi e nel controllo strumenti, organizzati da una prosp...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Sviluppo di applicazioni Windows
Gli strumenti di integrazione e monitoraggio dei file basati su FileSystemWatcher sono un argomento reale ricorrente frequentemente all'interno del nostro lavoro di sviluppo di applicazioni Windows.
Consulenza tecnica e revisione del progetto
Se si desidera organizzare contromisure per eventi mancati, nuove scansioni e rilevamento del completamento come progetto, questo si adatta bene alla consulenza tecnica e alla revisione del progetto.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- Perché FileSystemWatcher perde gli eventi?
- FileSystemWatcher ha un buffer interno e quando le modifiche si concentrano in una breve finestra, si verifica un overflow e le singole notifiche vengono eliminate. Il buffer predefinito è 8192 byte e non può superare 64 KB e utilizza memoria non paginata, quindi ingrandirlo non è una vera soluzione. Una volta che si verifica un overflow, la completezza dell'intero flusso di eventi diventa sospetta, quindi la risposta giusta è una nuova scansione completa della directory anziché fidarsi della cronologia per evento. Anche i file posizionati prima dell'avvio dell'app non vengono mai segnalati, motivo per cui è essenziale una scansione all'avvio.
- Posso considerare l'evento Created come un segnale che il file è pronto per la lettura?
- No. `Created` si attiva nel momento in cui viene creato il file, ovvero durante le copie e i trasferimenti mentre il contenuto è ancora in fase di scrittura, con uno o più eventi `Changed` successivi. La lettura a quel punto produce righe mancanti, file JSON corrotti o file ZIP corrotti. Il completamento dovrebbe essere reso esplicito dal lato del mittente tramite temp -> chiudi -> rinomina / sostituisci, o con un file o un manifest completato, piuttosto che dedotto dagli eventi.
- Perché FileSystemWatcher attiva la modifica più volte per un salvataggio?
- Non è garantito che Changed venga attivato esattamente una volta: anche le operazioni ordinarie come lo spostamento o il salvataggio di un file possono essere visualizzate come più eventi, una ridenominazione può produrre un evento Changed e il software antivirus o gli indicizzatori che toccano il file ne aggiungono altri. Anche l'ordine non è garantito e RenamedEventArgs.Name può anche essere nullo. Quindi non fidarti del conteggio o dell'ordine degli eventi; riunisci tutte le notifiche in un'unica richiesta di nuova scansione "vai a vedere" e assorbi i duplicati con idempotenza.
- Qual è la struttura consigliata per l'elaborazione dei file con FileSystemWatcher?
- Mantieni i gestori di eventi leggeri: dovrebbero accodare solo una richiesta di nuova scansione, con un singolo lavoratore che esegue la scansione effettiva della directory dopo aver unito i burst per circa 100-300 millisecondi. Eseguire una nuova scansione completa all'avvio, in caso di evento Errore e dopo aver ricreato l'osservatore. Con più lavoratori, assumi la proprietà rinominando atomicamente un file o un bundle dall'ingresso in una directory di elaborazione per lavoratore prima della lettura e registra una chiave di idempotenza in modo che la rielaborazione dello stesso input non conteggi mai due volte i risultati.
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.