c# - 为什么 File.ReadAllLinesAsync() 会阻塞 UI 线程?

标签 c# wpf asynchronous async-await .net-core-3.1

这是我的代码。读取文件行的​​ 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/

相关文章:

c# - 将项目添加到底层 ObservableCollection 时,递归 TreeView 不保留结构

wpf - 如何在资源字典的datatemplate中添加事件处理程序来控制

WPF Window.Close() 不触发 UserControl.Unloaded 事件

json - React Native - 返回 AsyncStorage 中的所有 JSON 数据?

c# - 将数据从数据层获取到 View 中的 observablecollection

c# - 带有变量名歧义的奇怪 C# 编译器问题

c# - 如何获取所有元素以及它们是否已分配?

c# - HttpWebResponse 中永无止境的 Stream

javascript - 这种不正常的行为是由 Javascript 异步运行引起的吗?

.net - 在 silverlight 上异步调用同步 WCF 操作协定方法