为何带Task.Run的ReadAllBytesAsync多文件读取比无Task.Run版本快近一倍?
为什么带Task.Run的ReadAllBytesAsync版本比直接调用快近一倍?
我尝试用File.ReadAllBytesAsync读取两个100MB的二进制文件(test2.bin是test1.bin的副本),发现带Task.Run的ReadAll2方法比直接调用的ReadAll1快近一倍,原本以为两者效果相当。性能测试数据如下:
| 方法 | 平均值 | 误差 | 标准差 | 内存分配 |
|---|---|---|---|---|
| ReadAll1 | 81.57 ms | 1.385 ms | 1.942 ms | 199.37 MB |
| ReadAll2 | 44.55 ms | 1.041 ms | 3.021 ms | 199.37 MB |
对应的测试代码:
public static async Task ReadAll1() { var tasks = new List<Task>(); tasks.Add(File.ReadAllBytesAsync(@"test1.bin")); tasks.Add(File.ReadAllBytesAsync(@"test2.bin")); await Task.WhenAll(tasks); }
public static async Task ReadAll2() { var tasks = new List<Task>(); tasks.Add(Task.Run(() => File.ReadAllBytesAsync(@"test1.bin"))); tasks.Add(Task.Run(() => File.ReadAllBytesAsync(@"test2.bin"))); await Task.WhenAll(tasks); }
核心原因在于File.ReadAllBytesAsync的同步前置操作并行化:
File.ReadAllBytesAsync虽是异步IO方法,但它的初始阶段(打开文件、校验权限、初始化文件流等)是同步执行的,这部分操作会占用当前线程直到完成。- 在
ReadAll1中,两个ReadAllBytesAsync在同一个线程上依次启动,导致两个文件的打开操作串行执行——第一个文件打开完成后才会启动第二个文件的打开流程,后续的异步读取也因此延迟,总耗时接近两次读取的时间之和。 - 在
ReadAll2中,Task.Run将每个ReadAllBytesAsync的启动(包括同步的打开阶段)放到独立的线程池线程上执行,两个文件的打开和读取操作可以真正并行启动,总耗时接近单次文件读取的时间,因此速度快了近一倍。
补充说明:这种差异在文件较大时更明显,因为同步前置操作的时间占比相对读取本身的耗时更小,并行启动后整体效率提升更显著。两个方法的内存分配一致,说明最终的读取逻辑没有区别,差异仅来自任务启动阶段的并行度。
内容的提问来源于stack exchange,提问作者user1522446
相关产品推荐
相关产品推荐

