Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Usando la funzionalità asincrona per accedere ai file, è possibile chiamare metodi asincroni senza usare callback o suddividere il codice tra più metodi o espressioni lambda. Per rendere asincrono il codice sincrono, chiamare un metodo asincrono anziché un metodo sincrono e aggiungere alcune parole chiave al codice.
Prendere in considerazione l'aggiunta di asincronia alle chiamate di accesso ai file per questi motivi:
- Asincronia rende le applicazioni dell'interfaccia utente più reattive perché il thread dell'interfaccia utente che avvia l'operazione può eseguire altre operazioni. Se il thread dell'interfaccia utente deve eseguire codice che richiede molto tempo (ad esempio, più di 50 millisecondi), l'interfaccia utente potrebbe bloccarsi fino al completamento dell'I/O e il thread dell'interfaccia utente può elaborare nuovamente l'input della tastiera e del mouse e altri eventi.
- Asincronia migliora la scalabilità di ASP.NET e altre applicazioni basate su server riducendo la necessità di thread. Se l'applicazione usa un thread dedicato per risposta e vengono gestite contemporaneamente migliaia di richieste, sono necessari mille thread. Le operazioni asincrone spesso non devono usare un thread durante l'attesa. Usano brevemente il thread di completamento di I/O esistente alla fine.
- La latenza di un'operazione di accesso ai file potrebbe essere molto bassa in condizioni correnti, ma la latenza potrebbe aumentare notevolmente in futuro. Ad esempio, un file potrebbe essere spostato in un server di tutto il mondo.
- L'overhead aggiunto dell'uso della caratteristica async è minimo.
- È possibile eseguire più operazioni di I/O asincrone senza bloccare il thread chiamante.
Usare le classi appropriate
Gli esempi semplici in questo argomento illustrano File.WriteAllTextAsync e File.ReadAllTextAsync. Per controllare correttamente le operazioni di I/O del file, usare la FileStream classe , che dispone di un'opzione che causa l'esecuzione di I/O asincroni a livello di sistema operativo. Usando questa opzione, è possibile evitare di bloccare in molti casi un thread del pool di thread. Per abilitare questa opzione, specificare l'argomento useAsync=true o options=FileOptions.Asynchronous nella chiamata al costruttore.
Non è possibile usare questa opzione con StreamReader e StreamWriter se le si apre direttamente specificando un percorso di file. Tuttavia, è possibile usare questa opzione se si fornisce un Stream che la classe FileStream ha aperto. Le chiamate asincrone sono più veloci nelle applicazioni dell'interfaccia utente anche se un thread del pool è bloccato, perché il thread dell'interfaccia utente non viene bloccato durante l'attesa.
Scrivere testo
Gli esempi seguenti scrivono testo in un file. A ogni istruzione await, il metodo viene immediatamente chiuso. Al termine dell'I/O del file, il metodo riprende in corrispondenza dell'istruzione che segue l'istruzione await. Il modificatore asincrono è nella definizione dei metodi che usano l'istruzione await.
Esempio semplice
public async Task SimpleWriteAsync()
{
string filePath = "simple.txt";
string text = $"Hello World";
await File.WriteAllTextAsync(filePath, text);
}
Esempio di controllo finito
public async Task ProcessWriteAsync()
{
string filePath = "temp.txt";
string text = $"Hello World{Environment.NewLine}";
await WriteTextAsync(filePath, text);
}
async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
L'esempio originale ha l'istruzione await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);, ovvero una contrazione delle due istruzioni seguenti:
Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;
La prima istruzione restituisce un'attività e determina l'avvio dell'elaborazione di file. La seconda istruzione con await determina l'uscita immediata del metodo e la restituzione di un'attività diversa. Al termine dell'elaborazione del file, l'esecuzione torna all'istruzione che segue l'await.
Leggere il testo
Gli esempi seguenti leggono testo da un file.
Esempio semplice
public async Task SimpleReadAsync()
{
string filePath = "simple.txt";
string text = await File.ReadAllTextAsync(filePath);
Console.WriteLine(text);
}
Esempio di controllo finito
Il testo viene memorizzato nel buffer e, in questo caso, inserito in un oggetto StringBuilder. A differenza dell'esempio precedente, la valutazione di await produce un valore. Il ReadAsync metodo restituisce unTask<Int32>, così la valutazione dell'operatore await produce un Int32 valore numRead al termine dell'operazione. Per ulteriori informazioni, consultare Tipi restituiti asincroni (C#).
public async Task ProcessReadAsync()
{
try
{
string filePath = "temp.txt";
if (File.Exists(filePath) != false)
{
string text = await ReadTextAsync(filePath);
Console.WriteLine(text);
}
else
{
Console.WriteLine($"file not found: {filePath}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
async Task<string> ReadTextAsync(string filePath)
{
using var sourceStream =
new FileStream(
filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true);
var sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
Più operazioni di I/O asincrone
Gli esempi seguenti avviano più operazioni di scrittura asincrone. Il runtime accoda queste operazioni e l'implementazione sottostante potrebbe usare thread di I/O asincroni del sistema operativo o del pool di thread a seconda della piattaforma e della configurazione, quindi la concorrenza effettiva dipende dal sistema operativo e dall'hardware.
Esempio semplice
public async Task SimpleParallelWriteAsync()
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 11; index <= 20; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
writeTaskList.Add(File.WriteAllTextAsync(filePath, text));
}
await Task.WhenAll(writeTaskList);
}
Esempio di controllo finito
Per ogni file, il WriteAsync metodo restituisce un'attività aggiunta a un elenco di attività. L'istruzione await Task.WhenAll(tasks); esce dal metodo e riprende al suo interno una volta completata l'elaborazione dei file per tutte le attività.
L'esempio chiude tutte le FileStream istanze in un blocco finally una volta completate le attività. Se ogni FileStream fosse creata in una dichiarazione using, FileStream potrebbe essere deallocata prima del completamento dell'attività.
L'approccio asincrono evita di bloccare il thread di chiamata mentre l'I/O è in sospeso. In molti casi, i miglioramenti della velocità effettiva dipendono dal sistema operativo, dall'hardware e da alcune piattaforme, dal comportamento di runtime .NET, ad esempio i limiti del pool di thread e la pianificazione.
public async Task ProcessMultipleWritesAsync()
{
IList<FileStream> sourceStreams = new List<FileStream>();
try
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 1; index <= 10; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
byte[] encodedText = Encoding.Unicode.GetBytes(text);
var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
Task writeTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
sourceStreams.Add(sourceStream);
writeTaskList.Add(writeTask);
}
await Task.WhenAll(writeTaskList);
}
finally
{
foreach (FileStream sourceStream in sourceStreams)
{
sourceStream.Close();
}
}
}
Quando si usano i metodi WriteAsync e ReadAsync, si può specificare un CancellationToken per annullare l'operazione a metà. Per altre informazioni, vedere Annullamento nei thread gestiti.