Ich habe den folgenden Code:
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
var s = File.ReadAllLines("Words.txt").ToList(); // my WPF app hangs here
// do something with s
button1.IsEnabled = true;
}
Words.txt
eine Tonne von Worten hat , die ich in den s - Variable lesen, versuche ich zu nutzen zu machen async
und await
Schlüsselwörter in C # 5 verwendet , Async CTP Library
so dass die WPF - Anwendung nicht hängen. Bisher habe ich folgenden Code:
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
Task<string[]> ws = Task.Factory.FromAsync<string[]>(
// What do i have here? there are so many overloads
); // is this the right way to do?
var s = await File.ReadAllLines("Words.txt").ToList(); // what more do i do here apart from having the await keyword?
// do something with s
button1.IsEnabled = true;
}
Ziel ist es, die Datei nicht synchron, sondern asynchron zu lesen, um ein Einfrieren der WPF-App zu vermeiden.
Jede Hilfe wird geschätzt, danke!
c#
.net
asynchronous
async-await
c#-5.0
Khellang
quelle
quelle
ToList()
nicht nur das Array, sondern erstellt einList
. Ohne weitere Informationen können Sie nicht davon ausgehen, dass dies unnötig ist, da möglicherweise Methoden// do something with s
aufgerufenList
werden.Antworten:
UPDATE : Async - Versionen
File.ReadAll[Lines|Bytes|Text]
,File.AppendAll[Lines|Text]
undFile.WriteAll[Lines|Bytes|Text]
wurden nun in .NET Kern verschmolzen und versendet mit .NET Core 2.0. Sie sind auch in .NET Standard 2.1 enthalten.Die Verwendung von asynchronen Wrappern, für
Task.Run
die es sich im Wesentlichen um einen Wrapper handeltTask.Factory.StartNew
, ist ein Codegeruch .Wenn Sie keinen CPU-Thread mithilfe einer Blockierungsfunktion verschwenden möchten, sollten Sie auf eine wirklich asynchrone E / A-Methode
StreamReader.ReadToEndAsync
wie folgt warten :using (var reader = File.OpenText("Words.txt")) { var fileText = await reader.ReadToEndAsync(); // Do something with fileText... }
Dadurch wird die gesamte Datei als
string
statt als erhaltenList<string>
. Wenn Sie stattdessen Zeilen benötigen, können Sie die Zeichenfolge anschließend wie folgt aufteilen:using (var reader = File.OpenText("Words.txt")) { var fileText = await reader.ReadToEndAsync(); return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None); }
BEARBEITEN : Hier sind einige Methoden, um den gleichen Code wie
File.ReadAllLines
auf wirklich asynchrone Weise zu erzielen . Der Code basiert auf der Implementierung von sichFile.ReadAllLines
selbst:using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; public static class FileEx { /// <summary> /// This is the same default buffer size as /// <see cref="StreamReader"/> and <see cref="FileStream"/>. /// </summary> private const int DefaultBufferSize = 4096; /// <summary> /// Indicates that /// 1. The file is to be used for asynchronous reading. /// 2. The file is to be accessed sequentially from beginning to end. /// </summary> private const FileOptions DefaultOptions = FileOptions.Asynchronous | FileOptions.SequentialScan; public static Task<string[]> ReadAllLinesAsync(string path) { return ReadAllLinesAsync(path, Encoding.UTF8); } public static async Task<string[]> ReadAllLinesAsync(string path, Encoding encoding) { var lines = new List<string>(); // Open the FileStream with the same FileMode, FileAccess // and FileShare as a call to File.OpenText would've done. using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, DefaultOptions)) using (var reader = new StreamReader(stream, encoding)) { string line; while ((line = await reader.ReadLineAsync()) != null) { lines.Add(line); } } return lines.ToArray(); } }
quelle
lines.ToArray
.Verwenden Sie Stream.ReadAsync zum asynchronen Lesen von Dateien.
private async void Button_Click(object sender, RoutedEventArgs e) { string filename = @"c:\Temp\userinputlog.txt"; byte[] result; using (FileStream SourceStream = File.Open(filename, FileMode.Open)) { result = new byte[SourceStream.Length]; await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length); } UserInput.Text = System.Text.Encoding.ASCII.GetString(result); }
Lesen Sie MSDN Stream.ReadAsync
quelle
Wenn Sie alle Zeilen asynchron aus der Datei lesen möchten, können Sie die
async
Funktion verwenden, um mit using auf die Datei zuzugreifenFileStream
.private static async Task<string[]> ReadAllLinesAsync(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().Split(new[] { Environment.NewLine },StringSplitOptions.None); } }
Sie können die
async
Methode im Ereignishandler verwenden, indem Sieasync
Ihre Ereignishandlerfunktion angeben.Hier erfahren Sie, wie Sie dies verwenden können, damit Ihr GUI-Thread nicht einfriert.
private async void button1_Click(object sender, RoutedEventArgs e) { button1.IsEnabled = false; var s = await ReadAllLinesAsync("Words.txt").ToList(); // do something with s button1.IsEnabled = true; }
Weitere Informationen finden Sie in den MS-Dokumenten
quelle
Ich bin auch auf ein in Ihrer Frage beschriebenes Problem gestoßen. Ich habe es nur einfacher gelöst als in früheren Antworten:
string[] values; StorageFolder folder = ApplicationData.Current.LocalFolder; // Put your location here. IList<string> lines = await FileIO.ReadLinesAsync(await folder.GetFileAsync("Words.txt");); lines.CopyTo(values, 0);
quelle
ApplicationData
und woherFileIO
? Sie scheinen nicht Teil des .Net Frameworks zu sein.ApplicationData
scheint aus dem UWP Framework zu stammen . Das heißt, Sie können nichtApplicationData
in einer "normalen" .net-Anwendung verwenden.FileIO
existiert in der VisualBasic-Assembly , hat aber meines Erachtens keine asynchronen Methoden. Woher beziehen Sie sie?Versuche dies:
private async void button1_Click(object sender, RoutedEventArgs e) { button1.IsEnabled = false; try { var s = await Task.Run(() => File.ReadAllLines("Words.txt").ToList()); // do something with s } finally { button1.IsEnabled = true; } }
Bearbeiten:
Sie brauchen den Versuch nicht, damit dies funktioniert. Es ist wirklich nur die eine Zeile, die Sie ändern müssen. So erklären Sie, wie es funktioniert: Dadurch wird ein anderer Thread erzeugt (tatsächlich wird einer aus dem Thread-Pool abgerufen) und dieser Thread wird zum Lesen der Datei aufgefordert. Wenn die Datei mit dem Lesen fertig ist, wird der Rest der button1_Click-Methode (aus dem GUI-Thread) mit dem Ergebnis aufgerufen. Beachten Sie, dass dies wahrscheinlich nicht die effizienteste Lösung ist, aber wahrscheinlich die einfachste Änderung an Ihrem Code, die die GUI nicht blockiert.
quelle
Task.Factory.StartNew(() => 'Some Task')
async
das volle Potenzial ausgeschöpft, da es immer noch einen Thread blockiert.Task.Run()
stattTask.Factory.StartNew
. Ich stimme der Thread-Blockierung voll und ganz zu. Ob das ein Problem ist oder nicht, hängt von der Situation ab (wie Sie sagen). Für das Lesen einiger Dateien über die GUI halte ich den Overhead für vernachlässigbar.