Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Mithilfe des asynchronen Features für den Zugriff auf Dateien können Sie asynchrone Methoden aufrufen, ohne Rückrufe zu verwenden oder den Code auf mehrere Methoden oder Lambda-Ausdrücke aufzuteilen. Um synchronen Code asynchron zu machen, rufen Sie eine asynchrone Methode anstelle einer synchronen Methode auf, und fügen Sie dem Code einige Schlüsselwörter hinzu.
Erwägen Sie das Hinzufügen einer Asynchronität zu Dateizugriffsaufrufen aus folgenden Gründen:
- Asynchrony macht UI-Anwendungen reaktionsfähiger, da der UI-Thread, der den Vorgang startet, andere Aufgaben ausführen kann. Wenn der UI-Thread Code ausführen muss, der lange dauern muss (z. B. mehr als 50 Millisekunden), kann die Benutzeroberfläche eingefroren werden, bis die E/A abgeschlossen ist, und der UI-Thread kann die Tastatur- und Mauseingabe und andere Ereignisse erneut verarbeiten.
- Asynchrony verbessert die Skalierbarkeit von ASP.NET und anderen serverbasierten Anwendungen, indem die Notwendigkeit von Threads reduziert wird. Wenn die Anwendung pro Antwort einen dedizierten Thread verwendet und tausend Anforderungen gleichzeitig verarbeitet werden, werden tausend Threads benötigt. Asynchrone Vorgänge müssen während der Wartezeit häufig keinen Thread verwenden. Sie verwenden den vorhandenen E/A-Abschlussthread kurz am Ende.
- Die Latenz eines Dateizugriffsvorgangs kann unter aktuellen Bedingungen sehr niedrig sein, die Latenz kann sich jedoch in Zukunft erheblich erhöhen. Beispielsweise kann eine Datei auf einen Server verschoben werden, der sich auf der ganzen Welt befindet.
- Der zusätzliche Aufwand für die Verwendung des Async-Features ist klein.
- Mehrere asynchrone E/A-Vorgänge können ausgeführt werden, ohne den aufrufenden Thread zu blockieren.
Verwenden geeigneter Klassen
Die einfachen Beispiele in diesem Thema veranschaulichen File.WriteAllTextAsync und File.ReadAllTextAsync. Um die Datei-E/A-Vorgänge zu steuern, verwenden Sie die FileStream Klasse, die über eine Option verfügt, die bewirkt, dass asynchrone E/A-Vorgänge auf Betriebssystemebene auftreten. Mit dieser Option können Sie das Blockieren eines ThreadPool-Threads in vielen Fällen vermeiden. Um diese Option zu aktivieren, geben Sie das useAsync=true Oder-Argument options=FileOptions.Asynchronous im Konstruktoraufruf an.
Sie können diese Option nicht mit StreamReader und StreamWriter verwenden, wenn Sie sie direkt durch Angabe eines Dateipfads öffnen. Allerdings können Sie diese Option verwenden, wenn Sie ihnen ein Stream geben, das die FileStream-Klasse geöffnet hat. Asynchrone Aufrufe sind in UI-Apps schneller, selbst wenn ein Thread aus dem Threadpool blockiert ist, da der UI-Thread während des Wartens nicht blockiert wird.
Text schreiben
In den folgenden Beispielen wird Text in eine Datei geschrieben. Bei jeder await-Anweisung wird die Methode sofort beendet. Wenn die Datei-E/A abgeschlossen ist, wird die Methode bei der Anweisung hinter der await-Anweisung fortgesetzt. Der asynchrone Modifizierer befindet sich in der Definition von Methoden, die die Await-Anweisung verwenden.
Einfaches Beispiel
public async Task SimpleWriteAsync()
{
string filePath = "simple.txt";
string text = $"Hello World";
await File.WriteAllTextAsync(filePath, text);
}
Beispiel für endliches Steuerelement
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);
}
Das ursprüngliche Beispiel weist die Anweisung await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);auf, die eine Kontraktion der folgenden beiden Anweisungen ist:
Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;
Die erste Anweisung gibt eine Aufgabe zurück und bewirkt, dass die Dateiverarbeitung gestartet wird. Die zweite await-Anweisung führt dazu, dass die Methode sofort beendet wird und eine andere Aufgabe zurückgibt. Wenn die Dateiverarbeitung später abgeschlossen ist, kehrt die Ausführung zur Anweisung zurück, die dem await folgt.
Lesen von Text
In den folgenden Beispielen wird Text aus einer Datei gelesen.
Einfaches Beispiel
public async Task SimpleReadAsync()
{
string filePath = "simple.txt";
string text = await File.ReadAllTextAsync(filePath);
Console.WriteLine(text);
}
Beispiel für endliches Steuerelement
Der gepufferte Text wird in diesem Fall in ein StringBuilder verschoben. Im Gegensatz zum vorherigen Beispiel erzeugt die Auswertung des Await einen Wert. Die Methode ReadAsync gibt ein Task<Int32>-Element zurück, sodass die Auswertung der await-Anweisung einen Int32-Wert vom Typ numRead erzeugt, nachdem der Vorgang abgeschlossen wurde. Weitere Informationen finden Sie unter Async-Rückgabetypen (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();
}
Mehrere asynchrone E/A-Vorgänge
In den folgenden Beispielen werden mehrere asynchrone Schreibvorgänge gestartet. Die Laufzeit reiht diese Vorgänge in Warteschlangen ein und die zugrunde liegende Implementierung verwendet je nach Plattform und Konfiguration möglicherweise asynchrone E/A- oder Threadpool-Threads, womit die tatsächliche Parallelität von der Betriebssystemplattform und der Hardware abhängt.
Einfaches Beispiel
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);
}
Beispiel für endliches Steuerelement
Für jede Datei gibt die WriteAsync Methode eine Aufgabe zurück, die einer Aufgabenliste hinzugefügt wird. Die await Task.WhenAll(tasks);-Anweisung beendet die Methode und wird innerhalb der Methode fortgesetzt, wenn die Dateiverarbeitung für alle Aufgaben abgeschlossen ist.
Im Beispiel werden alle FileStream Instanzen in einem finally Block geschlossen, nachdem die Aufgaben abgeschlossen wurden. Wenn jedes FileStream stattdessen in einer using-Anweisung erstellt wurde, könnte die FileStream möglicherweise freigegeben werden, bevor die Aufgabe abgeschlossen ist.
Der asynchrone Ansatz verhindert, dass der aufrufende Thread blockiert wird, während E/A aussteht. In vielen Fällen hängen Durchsatzverbesserungen vom Betriebssystem, der Hardware und auf einigen Plattformen vom .NET-Laufzeitverhalten ab, z. B. Threadpoolbeschränkungen und -planung.
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();
}
}
}
Wenn Sie die WriteAsync und ReadAsync Methoden verwenden, können Sie einen CancellationToken zum Abbrechen des Vorgangs während des Ablaufs angeben. Weitere Informationen finden Sie unter "Abbruch" in verwalteten Threads.