filestream同步写入会阻塞主线程,应改用异步+复用流;内存映射文件仅适用于≥16mb读多写少场景;异步io需禁用缓冲并启用asynchronous选项;路径拼接与编码需预处理以避免隐式开销。

用 FileStream 时别默认同步写入
同步写入(File.WriteAllText、StreamWriter 默认行为)在 IO 密集场景下会卡主线程,尤其写入大文件或高频小文件时,CPU 等待磁盘响应的时间远超计算时间。这不是代码逻辑错,是阻塞点被忽略了。
实操建议:
- 批量写入优先用
FileStream+BufferedStream,显式指定FileOptions.Asynchronous(注意:仅对ReadAsync/WriteAsync生效) - 避免在循环里反复打开/关闭文件——改用单个
FileStream复用,配合Seek或追加模式(FileMode.Append) - 写入前确认磁盘是否支持直接 I/O:NTFS 卷上启用
FILE_FLAG_NO_BUFFERING需要对齐缓冲区(BufferSize和偏移量必须是扇区大小整数倍,通常是 512 或 4096),否则抛IOException:“The parameter is incorrect.”
内存映射文件(MemoryMappedFile)不是万能加速器
它适合随机读取大文件(如日志分析、数据库页加载),但对顺序写入或小文件反而更慢——因为涉及页表映射、TLB 刷新和潜在的 FlushViewOfFile 开销。
实操建议:
- 只对 ≥ 16MB 的只读/读多写少文件考虑
MemoryMappedFile;小于 1MB 基本没收益 - 写入映射视图后,必须调用
mmf.CreateViewAccessor().Write后再Flush,否则数据可能滞留在内存未落盘 - 跨进程共享映射时,命名必须全局唯一,且注意 Windows 权限策略:默认非管理员进程无法创建全局命名对象,会报
UnauthorizedAccessException
异步 IO 不等于“加 async/await 就行”
FileStream.ReadAsync 和 WriteAsync 底层依赖操作系统完成端口(IOCP),但如果线程池被耗尽(比如大量同步 IO 混杂),异步回调会排队,实际变成伪异步。
实操建议:
- 禁用
FileStream的缓冲(bufferSize = 1)+ 启用FileOptions.Asynchronous才真正走 IOCP;否则仍可能走线程池模拟 - 不要在
async void方法里做文件 IO——异常会直接崩溃进程,改用async Task并确保调用链可 await - 用
ValueTask<int></int>替代Task<int></int>可减少小读写操作的分配压力,但仅当方法内部不 await 其他异步操作时才有效
路径和编码陷阱比想象中更早爆发
看似无关的 IO 性能问题,常源于路径解析或字符串编码。例如 Path.Combine("C:\data", "log.txt") 在 .NET 6+ 虽快,但若拼出带 Unicode 路径(如含中文目录),且目标卷是 FAT32,就可能触发额外的字符转换和权限检查。
实操建议:
- 避免运行时拼接路径——预编译为
ReadOnlySpan<char></char>或使用Path.Join(.NET 5+,无字符串分配) - 读取日志类文本文件时,明确指定
Encoding.UTF8,别依赖StreamReader自动检测(会读前 3 字节判断 BOM,增加一次系统调用) - 用
File.Exists做存在性检查?在高并发场景下它本身是竞争点——不如直接try/catch FileNotFoundException,省一次 syscall
真正卡住 IO 的地方,往往不在吞吐量数字里,而在你没意识到的路径合法性校验、编码回退、或 NTFS 稀疏文件属性自动触发的元数据更新。这些细节不报错,但会让吞吐曲线突然掉点。










