如何Async Files.ReadAllLines并等待结果?
我有以下代码,
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
有很多单词,我读入svariables,我试图使用async
并在C#5中使用Async CTP Library
await
关键字,所以WPF应用程序不会挂起。 到目前为止,我有以下代码,
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; }
目标是读取文件中的asynchronous而不是同步,以避免冻结WPF应用程序。
任何帮助表示赞赏,谢谢!
更新 : File.ReadAll[Lines|Bytes|Text]
, File.AppendAll[Lines|Text]
和File.WriteAll[Lines|Bytes|Text]
asynchronous版本现在已被合并到.NET Core中 。 这些方法将有望被移植到.NET Framework,Mono等,并被推广到.NET标准的未来版本中。
使用Task.Run
,它本质上是Task.Factory.StartNew
的包装,用于asynchronous包装是一种代码异味 。
如果您不想通过使用阻塞函数浪费CPU线程,则应等待真正的asynchronousIO方法StreamReader.ReadToEndAsync
,如下所示:
using (var reader = File.OpenText("Words.txt")) { var fileText = await reader.ReadToEndAsync(); // Do something with fileText... }
这将获得整个文件作为一个string
而不是一个List<string>
。 如果你需要线,你可以很容易地拆分string,如下所示:
using (var reader = File.OpenText("Words.txt")) { var fileText = await reader.ReadToEndAsync(); return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None); }
编辑 :这是一些实现与File.ReadAllLines
相同的代码的方法,但以真正asynchronous的方式。 该代码基于File.ReadAllLines
本身的实现:
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(); } }
我也遇到了你的问题中描述的问题。 我已经解决了它,只是简单地说,在以前的答案:
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);
尝试这个:
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; } }
编辑:
你不需要试试这个工作。 这实际上只是你需要改变的一行。 解释它是如何工作的:这产生了另一个线程(实际上从线程池获得一个线程)并获取该线程来读取文件。 当文件读完后,button1_Click方法的剩余部分被调用(从GUI线程),结果。 请注意,这可能不是最有效的解决scheme,但它可能是对代码的最简单的更改,不会阻止GUI。