为什么 File.ReadAllLinesAsync() 会阻塞 UI 线程? [英] Why File.ReadAllLinesAsync() blocks the UI thread?

查看:25
本文介绍了为什么 File.ReadAllLinesAsync() 会阻塞 UI 线程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的代码.读取文件行的​​ WPF 按钮的事件处理程序:

Here is my code. An event handler for WPF button that reads lines of a file:

private async void Button_OnClick(object sender, RoutedEventArgs e)
{
    Button.Content = "Loading...";
    var lines = await File.ReadAllLinesAsync(@"D:	emp.txt"); //Why blocking UI Thread???
    Button.Content = "Show"; //Reset Button text
}

我在 .NET Core 3.1 WPF 应用程序中使用了 File.ReadAllLines() 方法的异步版本.

I used asynchronous version of File.ReadAllLines() method in .NET Core 3.1 WPF App.

但是它阻塞了 UI 线程!为什么?

But it is blocking the UI Thread! Why?

更新:与@Theodor Zoulias 相同,我做了一个测试:

Update: Same as @Theodor Zoulias, I do a test :

private async void Button_OnClick(object sender, RoutedEventArgs e)
    {
        Button.Content = "Loading...";
        TextBox.Text = "";

        var stopwatch = Stopwatch.StartNew();
        var task = File.ReadAllLinesAsync(@"D:	emp.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 硬盘

.NET Core 3.1, C# 8, WPF, Debug build | 7.32 Mb File(.txt) | HDD 5400 SATA

推荐答案

遗憾的是,根据 Microsoft 的 自己的建议关于如何期望异步方法行为.

Sadly the built-in asynchronous APIs for accessing the filesystem are not implemented consistently according to Microsoft's own recommendations about how asynchronous methods are expected to behave.

基于 TAP 的异步方法可以在返回结果任务之前同步执行少量工作,例如验证参数和启动异步操作.同步工作应该保持在最低限度,以便异步方法可以快速返回.

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 之前阻塞当前线程相当长的时间.例如,在我的较旧的实验中,从我的 SSD,这个方法阻塞了调用线程 120 毫秒,返回一个 Task 然后在 20 毫秒后完成.我的建议是避免使用来自 GUI 应用程序的异步文件系统 API,而是使用包裹在 Task.Run.

Methods like StreamReader.ReadToEndAsync do not behave this way, and instead block the current thread for a considerable amount of time before returning an incomplete Task. For example in an older experiment of mine with reading a 6MB file from my SSD, this method blocked the calling thread for 120 msec, returning a Task that was then completed after only 20 msec. My suggestion is to avoid using the asynchronous filesystem APIs from GUI applications, and use instead the synchronous APIs wrapped in Task.Run.

var lines = await Task.Run(() => File.ReadAllLines(@"D:	emp.txt"));


更新:以下是一些使用 File.ReadAllLinesAsync:


Update: Here are some experimental results with 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毫秒后返回任务完成.多次运行后,这些测量结果是一致的.

The method File.ReadAllLinesAsync blocked the current thread for 450 msec, and the returned task completed after 5 msec. These measurements are consistent after multiple runs.

.NET Core 3.1.3、C# 8、控制台应用程序、发布版本(未附加调试器)、Windows 10、SSD Toshiba OCZ Arc 100 240GB

.NET Core 3.1.3, C# 8, Console App, Release build (no debugger attached), Windows 10, SSD Toshiba OCZ Arc 100 240GB

这篇关于为什么 File.ReadAllLinesAsync() 会阻塞 UI 线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆