Eventi
19 nov, 23 - 21 nov, 23
Ottenere il vantaggio competitivo necessario con potenti soluzioni di intelligenza artificiale e cloud partecipando a Microsoft Ignite online.
Iscriviti subitoQuesto browser non è più supportato.
Esegui l'aggiornamento a Microsoft Edge per sfruttare i vantaggi di funzionalità più recenti, aggiornamenti della sicurezza e supporto tecnico.
API importanti
Gli sviluppatori a volte si imbattono in una serie di problemi comuni quando usano i metodi di scrittura delle classi FileIO e PathIO per eseguire operazioni I/O del file system. I problemi comuni, ad esempio, includono:
I metodi di scrittura delle classi FileIO e PathIO includono:
Questo articolo fornisce informazioni dettagliate sul funzionamento di questi metodi per aiutare gli sviluppatori a comprendere meglio quando e come usarli. Questo articolo fornisce linee guida e non tenta di fornire una soluzione per tutti i possibili problemi I/O dei file.
Nota
Questo articolo illustra i metodi FileIO tramite esempi e discussioni. Tuttavia, i metodi PathIO seguono un modello simile e la maggior parte delle indicazioni fornite in questo articolo si applica anche a tali metodi.
Un oggetto StorageFile non è un punto di controllo dei file come il modello di programmazione Win32 nativo. Al contrario, un StorageFile è una rappresentazione di un file con i metodi che servono a modificarne il contenuto.
Comprendere questo concetto è utile quando si eseguono I/O con un StorageFile. Ad esempio, la sezione Writing to a file (Scrittura su file) illustra tre modi per scrivere in un file:
I primi due scenari sono quelli più comunemente usati dalle app. La scrittura su file in una singola operazione semplifica la codifica e la manutenzione, e grazie ad essa l'app non sarà più responsabile della gestione delle complessità dell'I/O dei file. Tuttavia, questa praticità comporta un costo: la perdita di controllo sull'intera operazione e la possibilità di rilevare gli errori in punti specifici.
I metodi di scrittura delle classi FileIO e PathIO aggiungono un ulteriore livello ai passaggi del terzo modello di scrittura descritto sopra. Questo livello è incapsulato in una transazione di archiviazione.
Per proteggere l'integrità del file originale nel caso in cui si verifichino problemi durante la scrittura dei dati, i metodi di scrittura usano un modello transazionale aprendo il file con OpenTransactedWriteAsync. Questo processo crea un oggetto StorageStreamTransaction. Dopo aver creato questo oggetto di transazione, le API scrivono i dati in modo simile nell'esempio Accesso al file o nell'esempio di codice nell'articolo StorageStreamTransaction.
Il diagramma seguente illustra le attività sottostanti eseguite dal metodo WriteTextAsync in un'operazione di scrittura con esito positivo. Questa illustrazione fornisce una vista semplificata dell'operazione. Ad esempio, salta passaggi come la codifica del testo e il completamento asincrono su thread diversi.
I vantaggi dell'uso dei metodi di scrittura delle classi FileIO e PathIO al posto del più complesso modello in quattro passaggi che usa un flusso sono:
Tuttavia, con così tanti punti di errore intermedi possibili, esiste una forte probabilità di errore. Quando si verifica un errore potrebbe essere difficile comprendere dove il processo non è riuscito. Le sezioni seguenti illustrano alcuni degli errori riscontrati quando si usano i metodi di scrittura e le relative soluzioni possibili.
Questa tabella presenta i codici di errore comuni in cui gli sviluppatori di app si imbattono quando usano i metodi di scrittura. I passaggi nella tabella corrispondono ai passaggi nel diagramma precedente.
Nome errore (valore) | Passaggi | Cause | Soluzioni |
---|---|---|---|
ERROR_ACCESS_DENIED (0X80070005) | 5 | Il file originale potrebbe essere contrassegnato per l'eliminazione, probabilmente da un'operazione precedente. | Ripetere l'operazione. Verificare che l'accesso al file sia sincronizzato. |
ERROR_SHARING_VIOLATION (0x80070020) | 5 | Il file originale viene aperto da un'altra scrittura esclusiva. | Ripetere l'operazione. Verificare che l'accesso al file sia sincronizzato. |
ERROR_UNABLE_TO_REMOVE_REPLACED (0x80070497) | 19-20 | Il file originale (file. txt) non può essere sostituito perché è in uso. Prima che questo potesse essere sostituito, un altro processo o operazione ha ottenuto l'accesso al file. | Ripetere l'operazione. Verificare che l'accesso al file sia sincronizzato. |
ERROR_DISK_FULL (0x80070070) | 7, 14, 16, 20 | Il modello sottoposto a transazione crea un file aggiuntivo consumando memoria aggiuntiva. | |
ERROR_OUTOFMEMORY (0x8007000E) | 14, 16 | Questa situazione può verificarsi a causa di più operazioni I/O in attesa o di file di grandi dimensioni. | L'errore potrebbe essere risolto adottando un approccio più granulare tramite il controllo del flusso. |
E_FAIL (0x80004005) | Qualsiasi | Varie | Ripetere l'operazione. Se il problema persiste, potrebbe trattarsi di un errore della piattaforma e l'app deve essere terminata perché si trova in uno stato incoerente. |
Oltre agli errori restituiti dai metodi di scrittura, di seguito sono riportate alcune linee guida su che cosa può aspettarsi un'app quando effettua la scrittura su file.
L'app non deve fare alcuna supposizione sui dati nel file durante l'operazione di scrittura. Il tentativo di accedere al file prima del completamento di un'operazione potrebbe causare incoerenze dei dati. L'app deve essere responsabile del rilevamento degli I/O in sospeso.
Se il file in cui si scrive viene usato anche da un lettore equilibrato (vale a dire, aperto con FileAccessMode.Read, le letture successive avranno esito negativo con un errore ERROR_OPLOCK_HANDLE_CLOSED (0x80070323). In alcuni casi le app provano a riaprire il file per leggerlo nuovamente durante l'operazione di scrittura. Ciò potrebbe comportare una race condition in cui il metodo di scrittura non riesce durante il tentativo di sovrascrivere il file originale perché non può essere sostituito.
L'app potrebbe non essere l'unica app che sta tentando di accedere a un file che si trova in una qualsiasi delle KnownFolders. Non c'è garanzia che se l'operazione ha esito positivo, il contenuto che un'app ha scritto nel file rimarrà costante la volta successiva che tenta di leggere il file. Inoltre, errori di condivisione e di accesso negato diventano più comuni in questo scenario.
È possibile ridurre le probabilità di errori di concorrenza se l'app usa i metodi di scrittura per i file nei suoi dati locali, ma è comunque necessario fare attenzione. Se più operazioni di scrittura vengono inviate contemporaneamente al file, non c'è garanzia su quali dati finiscono nel file. Per risolvere questo problema, è consigliabile che l'app serializzi le operazioni di scrittura sul file.
In alcuni casi, se l'operazione viene annullata in modo forzato (ad esempio, se l'app è stata sospesa o terminata dal sistema operativo), la transazione non è stata eseguita o chiusa in modo appropriato. Ciò può tralasciare i file con un'estensione (. ~ TMP). Provare a eliminare questi file temporanei (se presenti nei dati locali dell'app) quando si gestisce l'attivazione dell'app.
Alcuni errori possono diventare più prevalenti in base al tipo di file, la frequenza con cui vi si accede e le dimensioni del file. In generale, esistono tre categorie di file a cui l'app può accedere:
L'app ha il pieno controllo sulle prime due categorie di file, perché fanno parte dei file del pacchetto dell'app e sono accessibili dall'app in modo esclusivo. Per i file nell'ultima categoria, l'app deve tenere presente che altre app e servizi del sistema operativo possono accedere al file contemporaneamente.
A seconda dell'app, l'accesso ai file può variare in base alla frequenza:
Per le dimensioni del file, prendere in considerazione i dati sulle prestazioni nel grafico seguente per il metodo WriteBytesAsync. Questo grafico confronta il tempo necessario per completare un'operazione rispetto alle dimensioni del file, su una prestazione media di 10000 operazioni per dimensioni del file in un ambiente controllato.
I valori temporali sull'asse y vengono omessi intenzionalmente da questo grafico, poiché configurazioni e hardware diversi producono valori temporali assoluti diversi. Tuttavia, durante i nostri test abbiamo osservato costantemente queste tendenze:
L'app deve essere progettata in modo tale da gestire la sospensione se si desidera mantenere le informazioni di stato o i metadati per l'utilizzo nelle sessioni successive. Per informazioni generali sulla sospensione delle app, vedere App lifecycle (Ciclo di vita dell'app) e questo post di blog.
A meno che il sistema operativo conceda l'esecuzione estesa per l'app, quando l'app viene sospesa ha 5 secondi per rilasciare tutte le relative risorse e salvare i dati. Per l'affidabilità e l'esperienza utente migliori, considerare sempre che il tempo a disposizione per gestire le attività di sospensione è limitato. Tenere presenti le linee guida seguenti durante il periodo di tempo di 5 secondi per la gestione delle attività di sospensione:
Se l'app opera su una piccola quantità di dati di stato durante la sospensione, nella maggior parte dei casi è possibile usare i metodi di scrittura per scaricare i dati. Tuttavia, se l'app usa una grande quantità di dati relativi allo stato, è consigliabile usare dei flussi per archiviare direttamente i dati. Questo può contribuire a ridurre il ritardo introdotto dal modello transazionale dei metodi di scrittura.
Per un esempio, vedere l'esempio BasicSuspension.
Ecco alcuni esempi e altre risorse per scenari specifici.
Ecco un esempio di pseudocodice su come ripetere un'operazione di scrittura (C#), presupponendo che l'operazione di scrittura deve essere eseguita dopo che l'utente seleziona un file per il salvataggio:
Windows.Storage.Pickers.FileSavePicker savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.FileTypeChoices.Add("Plain Text", new List<string>() { ".txt" });
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
Int32 retryAttempts = 5;
const Int32 ERROR_ACCESS_DENIED = unchecked((Int32)0x80070005);
const Int32 ERROR_SHARING_VIOLATION = unchecked((Int32)0x80070020);
if (file != null)
{
// Application now has read/write access to the picked file.
while (retryAttempts > 0)
{
try
{
retryAttempts--;
await Windows.Storage.FileIO.WriteTextAsync(file, "Text to write to file");
break;
}
catch (Exception ex) when ((ex.HResult == ERROR_ACCESS_DENIED) ||
(ex.HResult == ERROR_SHARING_VIOLATION))
{
// This might be recovered by retrying, otherwise let the exception be raised.
// The app can decide to wait before retrying.
}
}
}
else
{
// The operation was cancelled in the picker dialog.
}
Il blog Programmazione parallela con .NET è un'ottima risorsa per consigli sulla programmazione parallela. In particolare, il post su AsyncReaderWriterLock descrive come mantenere l'accesso esclusivo a un file per operazioni di scrittura, consentendo l'accesso in lettura simultaneo. Tenere presente che la serializzazione degli I/O avrà un impatto sulle prestazioni.
Eventi
19 nov, 23 - 21 nov, 23
Ottenere il vantaggio competitivo necessario con potenti soluzioni di intelligenza artificiale e cloud partecipando a Microsoft Ignite online.
Iscriviti subito