异步IO同步执行

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

根据.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}");
        }

其输出将返回: 当全部 00:00:05.6254815 后循环 00:00:05.6234609

但是,将一行转换为:

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

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

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

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

c# asynchronous io
2个回答
0
投票

为什么您会看到这个结果。

这对于并行或其他方式运行的任务来说不是问题。它也与线程无关。相反,您在第一个示例中根本没有开始任务。

理解Task.WhenAll

重要的是要注意,

Task.WhenAll
实际上并没有为您启动任务。它只是等待那些已经开始的任务完成。

这解释了为什么您的第一个代码示例在

WhenAll
之前和之后具有几乎相同的时间戳。您已创建列表中的任务,但尚未启动它们。
WhenAll
几乎可以立即有效地完成,因为没有任何事情需要等待。事实上,如果您检查输出文件,您可能会发现它们不在那里。 (除非它们是上次跑步的宿醉。)

而在修改后的示例中,您在将每个任务添加到列表之前显式启动每个任务(使用

Task.Run
)。当他们点击
Task.WhenAll
时,他们就已经开始了。


-4
投票

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

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

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

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