C# 如何异步 Files.ReadAllLines 并等待结果?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13167934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to Async Files.ReadAllLines and await for results?
提问by
I have the following 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.txthas a ton of words which i read into the s variable, I am trying to make use of asyncand awaitkeywords in C# 5 using Async CTP Libraryso the WPF app doesn't hang. So far I have the following code,
Words.txt我读入了 s 变量的大量单词,我试图在 C# 5 中使用async和await关键字,Async CTP Library以便 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;
}
The goal is to read the file in async rather than sync, to avoid freezing of WPF app.
目标是以异步方式而不是同步方式读取文件,以避免 WPF 应用程序冻结。
Any help is appreciated, Thanks!
任何帮助表示赞赏,谢谢!
采纳答案by khellang
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 Coreand shipped with .NET Core 2.0. They are also included in .NET Standard 2.1.
UPDATE:的异步版本File.ReadAll[Lines|Bytes|Text],File.AppendAll[Lines|Text]而File.WriteAll[Lines|Bytes|Text]现在已经被合并到.NET的核心,并与.NET 2.0的核心出货。它们也包含在 .NET Standard 2.1 中。
Using Task.Run, which essentially is a wrapper for Task.Factory.StartNew, for asynchronous wrappers is a code smell.
UsingTask.Run本质上是 的包装器Task.Factory.StartNew,对于异步包装器来说是一种代码异味。
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:
如果您不想通过使用阻塞函数来浪费 CPU 线程,则应该等待一个真正的异步 IO 方法StreamReader.ReadToEndAsync,如下所示:
using (var reader = File.OpenText("Words.txt"))
{
var fileText = await reader.ReadToEndAsync();
// Do something with fileText...
}
This will get the whole file as a stringinstead of a List<string>. If you need lines instead, you could easily split the string afterwards, like this:
这会将整个文件作为 astring而不是List<string>. 如果您需要行,您可以在之后轻松拆分字符串,如下所示:
using (var reader = File.OpenText("Words.txt"))
{
var fileText = await reader.ReadToEndAsync();
return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}
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.ReadAllLinesitself:
编辑:这里有一些方法可以实现与 相同的代码File.ReadAllLines,但是以真正异步的方式。代码基于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();
}
}
回答by Mike
Try this:
尝试这个:
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;
}
}
Edit:
编辑:
You don't need the try-finally for this to work. It's really only the one line that you need to change. To explain how it works: This spawns another thread (actually gets one from the thread pool) and gets that thread to read the file. When the file is finished reading then the remainder of the button1_Click method is called (from the GUI thread) with the result. Note that this is probably not the most efficient solution, but it is probably the simplest change to your code which doesn't block the the GUI.
你不需要 try-finally 来让它工作。这实际上只是您需要更改的一行。解释它是如何工作的:这会产生另一个线程(实际上是从线程池中获取一个线程)并让该线程读取文件。当文件读完后, button1_Click 方法的其余部分将被调用(从 GUI 线程)并带有结果。请注意,这可能不是最有效的解决方案,但它可能是对代码的最简单更改,不会阻塞 GUI。
回答by Anton Bakulev
I also encountered a problem described in your question. I've solved it just simplier that in previous answers:
我也遇到了你的问题中描述的问题。我已经在以前的答案中更简单地解决了它:
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);
回答by Mak Ahmed
Use Stream.ReadAsync for asynchronous reading of file,
使用 Stream.ReadAsync 异步读取文件,
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);
}
回答by habib
If you want to read all the lines from file asynchronously then you can use the asyncfeature to access the file with using FileStream.
如果要异步读取文件中的所有行,则可以使用该async功能通过 using 访问文件FileStream。
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);
}
}
You can use the asyncmethod inside the event handler by specifying asyncto your event handler function.
您可以async通过指定async事件处理程序函数来使用事件处理程序内的方法。
Here is how you can use this and this would not let your GUI thread to freeze.
这是您如何使用它,这不会让您的 GUI 线程冻结。
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;
}
For more details please see MS Docs
有关更多详细信息,请参阅MS 文档

