不要在 Linux 文件 IO 任务上使用协程
在一项网络 IO 繁重的 Python 项目中,我大量使用了 gevent 的协程模型加速网络 IO ,效果比 Python 传统的线程池好很多。然而在后续数据预处理的部分遇到了问题。
我的项目结构是一个经典的单 provider 、多 consumer 模型,provider 负责读取一个超大文件(大于 8 Gb)并写入队列,然后多个 consumer 从队列读取数据并进行处理。如下所示
1 |
|
然而在运行的时候,程序总是阻塞在文件读操作,即 provider
上。也就是说,必须等待文件读入队列这个事件结束后,消费者才能从队列获取数据进行处理,完全无法满足并行(边读边处理)的需要。
经过调研,我发现在 Linux 系统下,和网络 IO 不同,文件 IO 一直是 ready 状态,不存在不可达或需要等待的问题。因此 Linux 会使一个文件 IO 线程不停进行 write/read 。由于这个文件 IO 的过程阻塞了执行的线程,进而全部阻塞了该线程中的所有协程。这就是我遇到了读取/写入一个文件,却导致所有协程阻塞的原因。
一篇文章指出,如果要实现文件 IO ,不得不至少实现多线程,没有其它选择——即使我们必须要面对 Python 臭名昭著的 GIL 锁。
gevent 的一个 issue 也说明了为何不支持非阻塞式的文件 IO。
我将程序改为线程池实现后,就成功解决了这个问题,实现了边读边处理的并行需求。
参考文章 https://blog.csdn.net/u014609111/article/details/118181367
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alrisha!