Using Async for File Access (C#) (Verwenden von Async für Dateizugriff (C#))Using Async for File Access (C#)

Sie können die Async-Funktion verwenden, um auf Dateien zuzugreifen.You can use the async feature to access files. Mithilfe der Async-Funktion können Sie asynchrone Methoden aufrufen, ohne Rückrufe zu verwenden oder den Code über mehrere Methoden oder Lambdaausdrücke teilen zu müssen.By using the async feature, you can call into asynchronous methods without using callbacks or splitting your code across multiple methods or lambda expressions. Um synchronen Code asynchron auszuführen, rufen Sie einfach eine asynchrone Methode anstelle einer synchronen Methode auf und fügen Sie dem Code einige Schlüsselwörter hinzu.To make synchronous code asynchronous, you just call an asynchronous method instead of a synchronous method and add a few keywords to the code.

Sie sollten die folgenden Gründe für das Hinzufügen von Asynchronie zu Dateizugriffsaufrufen in Betracht ziehen:You might consider the following reasons for adding asynchrony to file access calls:

  • Durch Asynchronie kann die Reaktionsfähigkeit von UI-Anwendungen verbessert werden, da der UI-Thread, der den Vorgang startet, andere Aufgaben durchführen kann.Asynchrony makes UI applications more responsive because the UI thread that launches the operation can perform other work. Wenn der UI-Thread Code ausführt, der viel Zeit benötigt (z.B. mehr als 50 Millisekunden), kann die UI einfrieren, bis der E/A-Vorgang abgeschlossen ist und der UI-Thread wieder Tastatur- und Mauseingaben und andere Ereignisse verarbeiten kann.If the UI thread must execute code that takes a long time (for example, more than 50 milliseconds), the UI may freeze until the I/O is complete and the UI thread can again process keyboard and mouse input and other events.

  • Asynchronie verbessert die Skalierbarkeit von ASP.NET und anderen serverbasierten Anwendungen, indem die Notwendigkeit von Threads reduziert wird.Asynchrony improves the scalability of ASP.NET and other server-based applications by reducing the need for threads. Wenn die Anwendung einen dedizierten Thread pro Antwort verwendet und tausend Anforderungen gleichzeitig behandelt werden, werden auch tausend Threads benötigt.If the application uses a dedicated thread per response and a thousand requests are being handled simultaneously, a thousand threads are needed. Asynchrone Vorgänge benötigen während der Wartezeit oft keinen Thread.Asynchronous operations often don’t need to use a thread during the wait. Sie verwenden den vorhandenen E/A-Abschlussthread kurz am Ende.They use the existing I/O completion thread briefly at the end.

  • Die Latenz von Dateizugriffsvorgängen kann unter bestimmten Umständen sehr niedrig sein, aber sie kann in Zukunft stark ansteigen.The latency of a file access operation might be very low under current conditions, but the latency may greatly increase in the future. Eine Datei kann z.B. auf einen Server auf der anderen Seite der Erde verschoben werden.For example, a file may be moved to a server that's across the world.

  • Der zusätzliche Mehraufwand durch Verwendung der Async-Funktion ist gering.The added overhead of using the Async feature is small.

  • Asynchrone Aufgaben können problemlos parallel ausgeführt werden.Asynchronous tasks can easily be run in parallel.

Ausführen der BeispieleRunning the Examples

Sie können eine WPF-Anwendung oder Windows Forms-Anwendung erstellen und dann eine Schaltfläche hinzufügen, um die Beispiele in diesem Thema auszuführen.To run the examples in this topic, you can create a WPF Application or a Windows Forms Application and then add a Button. Fügen Sie im Click-Ereignis der Schaltfläche einen Aufruf der ersten Methode jedes Beispiels hinzu.In the button's Click event, add a call to the first method in each example.

Nehmen Sie in den folgenden Beispielen die using-Anweisungen mit auf.In the following examples, include the following using statements.

using System;  
using System.Collections.Generic;  
using System.Diagnostics;  
using System.IO;  
using System.Text;  
using System.Threading.Tasks;  

Verwenden der FileStream-KlasseUse of the FileStream Class

In den Beispielen in diesem Thema wird die FileStream-Klasse verwendet, die über eine Option verfügt, die asynchrone E/A-Vorgänge auf Betriebssystemebene auslöst.The examples in this topic use the FileStream class, which has an option that causes asynchronous I/O to occur at the operating system level. Mit dieser Option können Sie das Blockieren eines ThreadPool-Threads in vielen Fällen vermeiden.By using this option, you can avoid blocking a ThreadPool thread in many cases. Geben Sie zum Aktivieren dieser Option das Argument useAsync=true oder options=FileOptions.Asynchronous im Konstruktoraufruf an.To enable this option, you specify the useAsync=true or options=FileOptions.Asynchronous argument in the constructor call.

Sie können diese Option nicht mit StreamReader und StreamWriter verwenden, wenn Sie sie direkt öffnen, indem Sie einen Dateipfad angeben.You can’t use this option with StreamReader and StreamWriter if you open them directly by specifying a file path. Allerdings können Sie diese Option verwenden, wenn Sie ihnen ein Stream geben, das die FileStream-Klasse geöffnet hat.However, you can use this option if you provide them a Stream that the FileStream class opened. Beachten Sie, dass asynchrone Aufrufe in Anwendungsbenutzeroberflächen schneller sind, selbst wenn ein ThreadPool-Thread blockiert wird, da der UI-Thread während der Wartezeit nicht blockiert ist.Note that asynchronous calls are faster in UI apps even if a ThreadPool thread is blocked, because the UI thread isn’t blocked during the wait.

Schreiben von TextWriting Text

Im folgenden Beispiel wird Text in eine Datei geschrieben.The following example writes text to a file. Bei jeder await-Anweisung wird die Methode sofort beendet.At each await statement, the method immediately exits. Wenn die Datei-E/A abgeschlossen ist, wird die Methode bei der Anweisung hinter der await-Anweisung fortgesetzt.When the file I/O is complete, the method resumes at the statement that follows the await statement. Beachten Sie, dass sich der async-Modifizierer in der Definition der Methoden befindet, die die await-Anweisung verwenden.Note that the async modifier is in the definition of methods that use the await statement.

public async void ProcessWrite()  
{  
    string filePath = @"temp2.txt";  
    string text = "Hello World\r\n";  

    await WriteTextAsync(filePath, text);  
}  

private async Task WriteTextAsync(string filePath, string text)  
{  
    byte[] encodedText = Encoding.Unicode.GetBytes(text);  

    using (FileStream sourceStream = new FileStream(filePath,  
        FileMode.Append, FileAccess.Write, FileShare.None,  
        bufferSize: 4096, useAsync: true))  
    {  
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);  
    };  
}  

Das ursprüngliche Beispiel verfügt über die Anweisung await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);, bei der es sich um eine Kontraktion der folgenden zwei Anweisungen handelt:The original example has the statement await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);, which is a contraction of the following two statements:

Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);  
await theTask;  

Die erste Anweisung gibt eine Aufgabe zurück und löst die Dateiverarbeitung aus.The first statement returns a task and causes file processing to start. Die zweite await-Anweisung führt dazu, dass die Methode sofort beendet wird und eine andere Aufgabe zurückgibt.The second statement with the await causes the method to immediately exit and return a different task. Wenn die Dateiverarbeitung später abgeschlossen wird, wird die Ausführung bei der Anweisung fortgesetzt, die auf die „await“-Anweisung folgt.When the file processing later completes, execution returns to the statement that follows the await. Weitere Informationen finden Sie unter Ablaufsteuerung in asynchronen Programmen (C#).For more information, see Control Flow in Async Programs (C#).

Lesen von TextReading Text

Im folgenden Beispiel wird Text aus einer Datei gelesen.The following example reads text from a file. Der Text wird gepuffert und in diesem Fall in StringBuilder abgelegt.The text is buffered and, in this case, placed into a StringBuilder. Anders als im vorherigen Beispiel generiert die Auswertung von „await“ einen Wert.Unlike in the previous example, the evaluation of the await produces a value. Die Methode ReadAsync gibt ein Task<Int32> zurück, sodass die Auswertung von await einen Int32-Wert (numRead) erzeugt, nachdem der Vorgang abgeschlossen ist.The ReadAsync method returns a Task<Int32>, so the evaluation of the await produces an Int32 value (numRead) after the operation completes. Weitere Informationen finden Sie unter Async Return Types (C#) (Asynchrone Rückgabetypen (C#)).For more information, see Async Return Types (C#).

public async void ProcessRead()  
{  
    string filePath = @"temp2.txt";  

    if (File.Exists(filePath) == false)  
    {  
        Debug.WriteLine("file not found: " + filePath);  
    }  
    else  
    {  
        try  
        {  
            string text = await ReadTextAsync(filePath);  
            Debug.WriteLine(text);  
        }  
        catch (Exception ex)  
        {  
            Debug.WriteLine(ex.Message);  
        }  
    }  
}  

private async Task<string> ReadTextAsync(string filePath)  
{  
    using (FileStream sourceStream = new FileStream(filePath,  
        FileMode.Open, FileAccess.Read, FileShare.Read,  
        bufferSize: 4096, useAsync: true))  
    {  
        StringBuilder 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();  
    }  
}  

Parallele Asynchrone E/AParallel Asynchronous I/O

Das folgende Beispiel zeigt die parallele Verarbeitung durch das Schreiben von zehn Textdateien.The following example demonstrates parallel processing by writing 10 text files. Die Methode WriteAsync gibt für jede Datei eine Aufgabe zurück, die anschließend zu einer Liste von Aufgaben hinzugefügt wird.For each file, the WriteAsync method returns a task that is then added to a list of tasks. Die await Task.WhenAll(tasks);-Anweisung beendet die Methode und wird innerhalb der Methode fortgesetzt, wenn die Dateiverarbeitung für alle Aufgaben abgeschlossen ist.The await Task.WhenAll(tasks); statement exits the method and resumes within the method when file processing is complete for all of the tasks.

Im Beispiel werden alle FileStream-Instanzen in einem finally-Block geschlossen, nachdem die Aufgaben abgeschlossen sind.The example closes all FileStream instances in a finally block after the tasks are complete. Wenn jeder FileStream stattdessen in einer using-Anweisung erstellt wurde, kann FileStream verworfen werden, bevor die Aufgabe abgeschlossen ist.If each FileStream was instead created in a using statement, the FileStream might be disposed of before the task was complete.

Beachten Sie, dass sich eine Steigerung der Leistung fast ausschließlich aus der parallelen und nicht der asynchronen Verarbeitung ergibt.Note that any performance boost is almost entirely from the parallel processing and not the asynchronous processing. Die Vorteile der Asynchronie sind, dass sie nicht mehrere Threads und den Benutzeroberflächenthread bindet.The advantages of asynchrony are that it doesn’t tie up multiple threads, and that it doesn’t tie up the user interface thread.

public async void ProcessWriteMult()  
{  
    string folder = @"tempfolder\";  
    List<Task> tasks = new List<Task>();  
    List<FileStream> sourceStreams = new List<FileStream>();  

    try  
    {  
        for (int index = 1; index <= 10; index++)  
        {  
            string text = "In file " + index.ToString() + "\r\n";  

            string fileName = "thefile" + index.ToString("00") + ".txt";  
            string filePath = folder + fileName;  

            byte[] encodedText = Encoding.Unicode.GetBytes(text);  

            FileStream sourceStream = new FileStream(filePath,  
                FileMode.Append, FileAccess.Write, FileShare.None,  
                bufferSize: 4096, useAsync: true);  

            Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);  
            sourceStreams.Add(sourceStream);  

            tasks.Add(theTask);  
        }  

        await Task.WhenAll(tasks);  
    }  

    finally  
    {  
        foreach (FileStream sourceStream in sourceStreams)  
        {  
            sourceStream.Close();  
        }  
    }  
}  

Bei Verwendung der Methoden WriteAsync und ReadAsync können Sie einen CancellationToken angeben, mit dem Sie den Vorgang in der Mitte des Streams beenden können.When using the WriteAsync and ReadAsync methods, you can specify a CancellationToken, which you can use to cancel the operation mid-stream. Weitere Informationen finden Sie unter Fine-Tuning Your Async Application (C#) (Abstimmen der asynchronen Anwendung (C#)) und Abbruch in verwalteten Threads.For more information, see Fine-Tuning Your Async Application (C#) and Cancellation in Managed Threads.

Siehe auchSee Also

Asynchronous Programming with async and await (C#) (Asynchrone Programmierung mit Async und Await (C#))Asynchronous Programming with async and await (C#)
Asynchrone Rückgabetypen (C#)Async Return Types (C#)
Ablaufsteuerung in asynchronen ProgrammenControl Flow in Async Programs (C#)