Condividi tramite


Uso di Async per l'accesso ai file (Visual Basic)

È possibile usare la funzionalità asincrona per accedere ai file. Usando la funzionalità asincrona, è possibile chiamare metodi asincroni senza usare callback o suddividere il codice tra più metodi o espressioni lambda. Per rendere asincrono il codice sincrono, è sufficiente chiamare un metodo asincrono anziché un metodo sincrono e aggiungere alcune parole chiave al codice.

È possibile considerare i motivi seguenti per aggiungere asincronia alle chiamate di accesso ai file:

  • 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 può 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, alla fine, il thread di completamento I/O esistente.

  • 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 può essere spostato in un server di tutto il mondo.

  • Il sovraccarico derivante dall’uso della funzionalità asincrona è ridotto.

  • Le attività asincrone possono essere eseguite facilmente in parallelo.

Esecuzione degli esempi

Per eseguire gli esempi in questo argomento, è possibile creare un'applicazione WPF o un'applicazione Windows Form e quindi aggiungere un pulsante. Nell'evento Click del pulsante, aggiungere una chiamata al primo metodo in ogni esempio.

Negli esempi seguenti, includi le seguenti istruzioni Imports.

Imports System  
Imports System.Collections.Generic  
Imports System.Diagnostics  
Imports System.IO  
Imports System.Text  
Imports System.Threading.Tasks  

Uso della classe FileStream

Gli esempi in questo argomento usano la FileStream classe , che include un'opzione che causa l'esecuzione di I/O asincroni a livello di sistema operativo. Usando questa opzione, è possibile evitare di bloccare un thread ThreadPool in molti casi. 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 loro un Stream che la classe FileStream ha aperto. Si noti che le chiamate asincrone sono più veloci nelle app dell'interfaccia utente anche se un thread ThreadPool è bloccato, perché il thread dell'interfaccia utente non viene bloccato durante l'attesa.

Scrittura di testo

Nell'esempio seguente viene scritto testo in un file. A ogni istruzione 'await', il metodo esce immediatamente. Al termine dell'I/O del file, il metodo riprende in corrispondenza dell'istruzione che segue l'istruzione await. Si noti che il modificatore asincrono è nella definizione dei metodi che usano l'istruzione await.

Public Async Sub ProcessWrite()  
    Dim filePath = "temp2.txt"  
    Dim text = "Hello World" & ControlChars.CrLf  
  
    Await WriteTextAsync(filePath, text)  
End Sub  
  
Private Async Function WriteTextAsync(filePath As String, text As String) As Task  
    Dim encodedText As Byte() = Encoding.Unicode.GetBytes(text)  
  
    Using sourceStream As New FileStream(filePath,  
        FileMode.Append, FileAccess.Write, FileShare.None,  
        bufferSize:=4096, useAsync:=True)  
  
        Await sourceStream.WriteAsync(encodedText, 0, encodedText.Length)  
    End Using  
End Function  

L'esempio originale ha l'istruzione Await sourceStream.WriteAsync(encodedText, 0, encodedText.Length), ovvero una contrazione delle due istruzioni seguenti:

Dim theTask As Task = 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 fa sì che il metodo esca immediatamente e restituisca un compito diverso. Al termine dell'elaborazione del file, l'esecuzione torna all'istruzione che segue l'await. Per altre informazioni, vedere Flusso di controllo in Programmi asincroni (Visual Basic).For more information, see Control Flow in Async Programs (Visual Basic).

Lettura del testo

Nell'esempio seguente viene letto testo da un file. 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 un Task<Int32>, in modo che la valutazione di await produca un Int32 valore (numRead) al termine dell'operazione. Per altre informazioni, vedere Tipi restituiti asincroni (Visual Basic).

Public Async Sub ProcessRead()  
    Dim filePath = "temp2.txt"  
  
    If File.Exists(filePath) = False Then  
        Debug.WriteLine("file not found: " & filePath)  
    Else  
        Try  
            Dim text As String = Await ReadTextAsync(filePath)  
            Debug.WriteLine(text)  
        Catch ex As Exception  
            Debug.WriteLine(ex.Message)  
        End Try  
    End If  
End Sub  
  
Private Async Function ReadTextAsync(filePath As String) As Task(Of String)  
  
    Using sourceStream As New FileStream(filePath,  
        FileMode.Open, FileAccess.Read, FileShare.Read,  
        bufferSize:=4096, useAsync:=True)  
  
        Dim sb As New StringBuilder  
  
        Dim buffer As Byte() = New Byte(&H1000) {}  
        Dim numRead As Integer  
        numRead = Await sourceStream.ReadAsync(buffer, 0, buffer.Length)  
        While numRead <> 0  
            Dim text As String = Encoding.Unicode.GetString(buffer, 0, numRead)  
            sb.Append(text)  
  
            numRead = Await sourceStream.ReadAsync(buffer, 0, buffer.Length)  
        End While  
  
        Return sb.ToString  
    End Using  
End Function  

I/O asincrono parallelo

Nell'esempio seguente viene illustrata l'elaborazione parallela scrivendo 10 file di testo. Per ogni file, il WriteAsync metodo restituisce un'attività che viene quindi aggiunta a un elenco di attività. L'istruzione Await Task.WhenAll(tasks) esce dal metodo e riprende all'interno del metodo al termine dell'elaborazione dei file per tutte le attività.

L'esempio chiude tutte le FileStream istanze in un blocco Finally una volta completati i compiti. Se ogni FileStream fosse invece creato in una dichiarazione Imports, il FileStream potrebbe essere smaltito prima che il compito sia completato.

Si noti che qualsiasi miglioramento delle prestazioni è quasi interamente dall'elaborazione parallela e non dall'elaborazione asincrona. I vantaggi dell'asincronia sono che non collega più thread e che non collega il thread dell'interfaccia utente.

Public Async Sub ProcessWriteMult()  
    Dim folder = "tempfolder\"  
    Dim tasks As New List(Of Task)  
    Dim sourceStreams As New List(Of FileStream)  
  
    Try  
        For index = 1 To 10  
            Dim text = "In file " & index.ToString & ControlChars.CrLf  
  
            Dim fileName = "thefile" & index.ToString("00") & ".txt"  
            Dim filePath = folder & fileName  
  
            Dim encodedText As Byte() = Encoding.Unicode.GetBytes(text)  
  
            Dim sourceStream As New FileStream(filePath,  
                FileMode.Append, FileAccess.Write, FileShare.None,  
                bufferSize:=4096, useAsync:=True)  
  
            Dim theTask As Task = sourceStream.WriteAsync(encodedText, 0, encodedText.Length)  
            sourceStreams.Add(sourceStream)  
  
            tasks.Add(theTask)  
        Next  
  
        Await Task.WhenAll(tasks)  
    Finally  
        For Each sourceStream As FileStream In sourceStreams  
            sourceStream.Close()  
        Next  
    End Try  
End Sub  

Quando si utilizzano i metodi WriteAsync e ReadAsync, è possibile specificare un CancellationToken oggetto, che puoi usare per annullare l'operazione nel corso del processo. Per ulteriori informazioni, vedere Fine-Tuning La tua applicazione asincrona (Visual Basic) e Annullamento nei thread gestiti.

Vedere anche