这是我的代码。读取文件行的 WPF 按钮的事件处理程序:
private async void Button_OnClick(object sender, RoutedEventArgs e)
{
Button.Content = "Loading...";
var lines = await File.ReadAllLinesAsync(@"D:\temp.txt"); //Why blocking UI Thread???
Button.Content = "Show"; //Reset Button text
}
我使用了 File.ReadAllLines()
的异步版本.NET Core 3.1 WPF 应用程序中的方法。但它阻塞了 UI 线程!为什么?
更新 :与@Theodor Zoulias 相同,我做了一个测试:
private async void Button_OnClick(object sender, RoutedEventArgs e)
{
Button.Content = "Loading...";
TextBox.Text = "";
var stopwatch = Stopwatch.StartNew();
var task = File.ReadAllLinesAsync(@"D:\temp.txt"); //Problem
var duration1 = stopwatch.ElapsedMilliseconds;
var isCompleted = task.IsCompleted;
stopwatch.Restart();
var lines = await task;
var duration2 = stopwatch.ElapsedMilliseconds;
Debug.WriteLine($"Create: {duration1:#,0} msec, Task.IsCompleted: {isCompleted}");
Debug.WriteLine($"Await: {duration2:#,0} msec, Lines: {lines.Length:#,0}");
Button.Content = "Show";
}
结果是:Create: 652 msec msec, Task.IsCompleted: False | Await: 15 msec, Lines: 480,001
.NET Core 3.1、C# 8、WPF、调试构建 | 7.32 Mb 文件(.txt) |硬盘 5400 SATA
最佳答案
遗憾的是,根据 Microsoft 的 own recommendations,用于访问文件系统的内置异步 API 并未一致实现。关于预期异步方法的行为方式。
An asynchronous method that is based on TAP can do a small amount of work synchronously, such as validating arguments and initiating the asynchronous operation, before it returns the resulting task. Synchronous work should be kept to the minimum so the asynchronous method can return quickly.
方法如
StreamReader.ReadToEndAsync
不要这样做,而是在返回不完整的 Task
之前阻塞当前线程相当长的时间。 .例如在 older experiment 中我从我的 SSD 读取 6MB 文件时,此方法阻塞了调用线程 120 毫秒,返回 Task
然后仅在 20 毫秒后完成。我的建议是避免使用 GUI 应用程序中的异步文件系统 API,而是使用包装在 Task.Run
中的同步 API。 .var lines = await Task.Run(() => File.ReadAllLines(@"D:\temp.txt"));
更新:以下是
File.ReadAllLinesAsync
的一些实验结果:var stopwatch = Stopwatch.StartNew();
var task = File.ReadAllLinesAsync(@"C:\6MBfile.txt");
var duration1 = stopwatch.ElapsedMilliseconds;
bool isCompleted = task.IsCompleted;
stopwatch.Restart();
var lines = await task;
var duration2 = stopwatch.ElapsedMilliseconds;
Console.WriteLine($"Create: {duration1:#,0} msec, Task.IsCompleted: {isCompleted}");
Console.WriteLine($"Await: {duration2:#,0} msec, Lines: {lines.Length:#,0}");
输出:Create: 450 msec, Task.IsCompleted: False
Await: 5 msec, Lines: 204,000
方法File.ReadAllLinesAsync
阻塞当前线程 450 毫秒,返回的任务在 5 毫秒后完成。这些测量在多次运行后是一致的。.NET Core 3.1.3、C# 8、控制台应用程序、发布版本(未附加调试器)、Windows 10、SSD Toshiba OCZ Arc 100 240GB
关于c# - 为什么 File.ReadAllLinesAsync() 会阻塞 UI 线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63217657/