我有以下代码,
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 有很多我读入 s 变量的单词,我正在尝试使用 async 和 awaitC# 5 中的关键字使用 Async CTP Library,因此 WPF 应用程序不会挂起.到目前为止,我有以下代码,
Words.txt has a ton of words which i read into the s variable, I am trying to make use of async and await keywords in C# 5 using Async CTP Library so the WPF app doesn't hang. So far I have the following 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; }目标是以异步而不是同步的方式读取文件,以避免 WPF 应用程序冻结.
The goal is to read the file in async rather than sync, to avoid freezing of WPF app.
感谢任何帮助,谢谢!
推荐答案UPDATE:File.ReadAll[Lines|Bytes|Text],File.AppendAll[Lines|Text] 和 File.WriteAll[Lines|Bytes|Text] 现在已经合并到 .NET Core 并随 .NET Core 2.0 一起提供.它们也包含在 .NET Standard 2.1 中.
UPDATE: Async versions of File.ReadAll[Lines|Bytes|Text], File.AppendAll[Lines|Text] and File.WriteAll[Lines|Bytes|Text] have now been merged into .NET Core and shipped with .NET Core 2.0. They are also included in .NET Standard 2.1.
使用 Task.Run,它本质上是 Task.Factory.StartNew 的包装器,用于异步包装器 是代码异味.
Using Task.Run, which essentially is a wrapper for Task.Factory.StartNew, for asynchronous wrappers is a code smell.
如果不想使用阻塞函数浪费一个 CPU 线程,应该等待真正的异步 IO 方法,StreamReader.ReadToEndAsync,像这样:
If you don't want to waste a CPU thread by using a blocking function, you should await a truly asynchronous IO method, StreamReader.ReadToEndAsync, like this:
using (var reader = File.OpenText("Words.txt")) { var fileText = await reader.ReadToEndAsync(); // Do something with fileText... }这会将整个文件作为 string 而不是 List.如果你需要行,你可以很容易地在之后拆分字符串,如下所示:
This will get the whole file as a string instead of a List<string>. If you need lines instead, you could easily split the string afterwards, like this:
using (var reader = File.OpenText("Words.txt")) { var fileText = await reader.ReadToEndAsync(); return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None); }EDIT:这里有一些方法可以实现与 File.ReadAllLines 相同的代码,但是以真正异步的方式.代码基于 File.ReadAllLines本身:
EDIT: Here are some methods to achieve the same code as File.ReadAllLines, but in a truly asynchronous manner. The code is based on the implementation of File.ReadAllLines itself:
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(); } }更多推荐
如何异步 Files.ReadAllLines 并等待结果?
发布评论