Dart并发Dio写文件报Too many open files(errno=24)错误如何解决
Too many open files (errno=24) 异常修复方案 这个异常的本质是进程同时持有的文件句柄数超过了操作系统给单进程分配的文件描述符上限,不需要硬改系统配置凑活,有成熟的落地方案,核心是控制并发IO量级,避免同时打开超量文件句柄,不需要从零手写复杂执行队列。
可直接落地的成熟方案
- 用现成的并发限流池管控全链路IO,不要裸用无限制的
Future.wait
直接用Dart生态通用的pool工具包创建固定大小的并发池,把「Dio拉取文件+本地写入」的完整逻辑作为单个任务提交到池内执行即可,并发数参考值:移动端设812,桌面端设1632,根据设备磁盘和网络性能微调,从根源上保证同一时间打开的文件句柄数不会触发系统阈值。
参考实现:import 'package:pool/pool.dart'; // 同一时间最多跑10个下载+写入任务 final ioTaskPool = Pool(10); Future<void> singleDownloadTask(String fileUrl, String localSavePath) async { return ioTaskPool.withResource(() async { final response = await dio.get( fileUrl, options: Options(responseType: ResponseType.bytes), ); // 直接调用writeAsBytes即可,方法内部会自动打开、写入、关闭文件句柄,不会泄漏 await File(localSavePath).writeAsBytes(response.data); }); } // 所有任务统一提交,并发池自动处理排队逻辑,不需要手动维护队列状态 await Future.wait(allDownloadTaskList.map( (task) => singleDownloadTask(task.url, task.savePath) )); - 修正文件写入的错误用法
不要用writeAsBytesSync做同步写入,同步阻塞会拉长单个文件句柄的持有时间,间接推高同时打开的文件数量;也不要提前手动打开文件流长期持有句柄,writeAsBytes默认实现已经处理了句柄的自动回收,不会出现资源泄漏。 - 兜底临时方案(不推荐作为最终修复)
如果是桌面端/服务端部署场景,可以临时调大单进程文件描述符上限,比如Linux环境执行ulimit -n 8192临时提高阈值,但这个方案只是延后异常触发,只要并发量级继续上涨还是会撞线,不能解决根本问题。
为什么不推荐无限制并发
无上限的批量并发会同时触发两个性能瓶颈,反而降低整体下载效率:
- 网络带宽被占满后,单个请求的超时率、失败率会明显上升
- 本地磁盘IO有最优并发阈值,超过阈值之后写入速度不会线性提升,反而会因为磁盘调度开销变大变慢,进一步拉长文件句柄持有时间,加剧fd耗尽的问题
不建议从零手写任务队列,成熟的并发池组件已经处理了任务排队、异常透传、超时释放、资源回收的全部边界逻辑,鲁棒性远高于临时手写的队列实现。
内容的提问来源于stack exchange,提问作者lynn
相关产品推荐
相关产品推荐

