C# 异步 IO 在 Windows 上同步执行

问题描述 投票:0回答:1

根据.NET文档,异步IO命令可用于并行执行文件操作:https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/using-async-for-文件访问#并行异步 io

然而,对上面的一个小修改表明情况并非如此:

  public static async Task SimpleParallelWriteByteAsync()
        {
            var rootFilePath = "C:/temp/asynctest2";
            var fileSize = 1024 * 1024 * 100;


            if (Directory.Exists(rootFilePath))
                Directory.Delete(rootFilePath, true);
            Directory.CreateDirectory(rootFilePath);
                        
            var buffer = new byte[fileSize];
            new Random().NextBytes(buffer);


            IList<Task> writeTaskList = new List<Task>();

            Stopwatch sw = Stopwatch.StartNew(); 
            for (int index = 0; index <= 10; index += 1)
            {
                string fileName = $"file-{index:00}.txt";
                string filePath = Path.Combine(rootFilePath, fileName);
                writeTaskList.Add(File.WriteAllBytesAsync(filePath, buffer));
            }
            var timespanLoop = sw.Elapsed;
            await Task.WhenAll(writeTaskList);
            var timespanAfterWhenall = sw.Elapsed;

            Console.WriteLine($"loop {timespanLoop} after when all {timespanAfterWhenall}");
        }

其输出将返回:

loop 00:00:05.6234609 after when all 00:00:05.6254815

在 Windows 上。

我在带有本地 SSD、网络驱动器、net472、net6.0 和 net8.0 的桌面上重现了这些数字之间几乎没有什么区别的相同行为(请在本地运行以重现!)。

但是,将一行转换为:

writeTaskList.Add(Task.Run(() => File.WriteAllBytesAsync(filePath, buffer)));

将为我们提供预期的并行输出:

loop 00:00:00.0089046 after when all 00:00:01.2339927

在 Linux 上,该行为正常工作:

00:00:00.0082008 after when all 00:00:03.0910144

我的问题是是什么在控制这个?为什么 .NET 文档是错误的/具有误导性的?在我的理解中,异步文件操作不是在线程中执行的,而是向操作系统发送调用以异步写入文件,然后操作系统会在完成时通知.NET,如下所示:https:// /blog.stephencleary.com/2013/11/there-is-no-thread.html

c# asynchronous io
1个回答
-5
投票

我认为误导的不是文档,而是运行 async 和运行 async/awaited 之间的区别。在原始代码中,文件写入确实是异步完成的,在它自己的线程上与正在等待它的上下文分开。在修改后的代码中,您正在触发任务,但不等待结果。

我认为人们在异步/等待模式中错过的是它创建了线程代码,但保留了人们更熟悉的同步编程实践。

例如,如果您观察线程计数,则修改后的版本将为循环中的每个“索引”生成一个新线程,而无需等待结果,而在原始版本中,它将为循环的每次迭代生成一个额外的线程,然后等待完成。

© www.soinside.com 2019 - 2024. All rights reserved.