我有以下代码,
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
在C#5中使用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;
}
目标是以异步方式读取文件而不是同步,以避免冻结WPF应用程序。
感谢任何帮助,谢谢!
答案 0 :(得分:111)
更新:File.ReadAll[Lines|Bytes|Text]
,File.AppendAll[Lines|Text]
和File.WriteAll[Lines|Bytes|Text]
的异步版本现已merged into .NET Core,并随.NET Core 2.0一起提供。这些方法在发布时也将成为.NET Standard 2.1的一部分。
使用Task.Run
,它本质上是Task.Factory.StartNew
的包装器,用于异步包装器is a code smell。
如果您不想通过阻塞功能浪费CPU线程,则应等待真正的异步IO方法StreamReader.ReadToEndAsync
,如下所示:
using (var reader = File.OpenText("Words.txt"))
{
var fileText = await reader.ReadToEndAsync();
// Do something with fileText...
}
这会将整个文件作为string
而不是List<string>
。如果您需要换行,您可以在之后轻松拆分字符串,如下所示:
using (var reader = File.OpenText("Words.txt"))
{
var fileText = await reader.ReadToEndAsync();
return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}
编辑:以下是一些与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();
}
}
答案 1 :(得分:0)
使用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);
}
答案 2 :(得分:0)
如果要异步读取文件中的所有行,则可以使用async
功能来使用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);
}
}
您可以通过在事件处理函数中指定async
来在事件处理函数中使用async
方法。
这是您可以使用的方式,这不会让您的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;
}
有关更多详细信息,请参见MS Docs
答案 3 :(得分:-2)
我也遇到了问题中描述的问题。我在以前的答案中解决了它的简单性:
driver
.elementByAccessibilityId('FOO')
答案 4 :(得分:-3)
试试这个:
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;
}
}
修改强>
你不需要try-finally就可以了。这真的只是你需要改变的一条线。解释它是如何工作的:这会生成另一个线程(实际上从线程池中获取一个)并获取该线程来读取该文件。当文件读完后,将调用button1_Click方法的其余部分(来自GUI线程)并显示结果。请注意,这可能不是最有效的解决方案,但它可能是对代码的最简单更改,它不会阻止GUI。