python多线程读取只读内存缓冲区绕过GIL

Posted

技术标签:

【中文标题】python多线程读取只读内存缓冲区绕过GIL【英文标题】:python multi-threads read a read-only memory buffer bypass GIL 【发布时间】:2016-11-01 04:34:23 【问题描述】:

我在磁盘上有很多文件需要读取,第一个选项是使用多线程,它在 SSD 上表现非常好。 (当线程被IO阻塞时,会释放GIL)

但我想在没有 SSD 的情况下实现类似或更快的速度,所以我将它们预加载到内存中(比如存储在 dict 中),每个线程都会从内存中读取每个文件内容。不幸的是,可能是因为 GIL,dict 中有一个锁,因此它的速度甚至比从 SSD 加载文件还要慢!

所以我的问题是,是否有任何解决方案可以创建没有锁/GIL 的只读内存缓冲区?像ramdisk或其他东西>

【问题讨论】:

如果你真的想要尽可能快的速度,那么用 C 或 C++ 或其他一些完全编译的语言重写你的程序(或至少是速度关键部分)怎么样?那么你将没有 GIL,也没有解释器开销,因为你将运行本机可执行文件。 【参考方案1】:

简而言之,没有。

尽管 Python(尤其是 CPython)是一种多线程语言,但解释器在任何时候都只能运行一段 Python 代码。因此,如果您的纯 Python 程序不包含阻塞 I/O(例如访问无锁内存缓冲区),那么无论您做什么,它都会降低单线程程序的性能。由于与其他线程同步存在开销,因此性能会比实际的单线程程序差。

(特别感谢 Graham Dumpleton!)solution 之一是为 CPython 编写 C 扩展。并在进入“C 领域”时释放 GIL。请注意,如果没有 GIL 保护,您将无法访问 python 内容,否则会导致细微的错误,或者直接崩溃。

有几个实现不使用 GIL,例如 Jython 和 Cython(不是 CPython)。您可以尝试使用它们。但请记住,编写正确的多线程程序是很困难的。编写一个快速的多线程程序更加困难。我的建议是编写多进程程序而不是多线程。并通过 IPC 左右传递数据(比如说 ZeroMQ,它易于使用且轻量级)。

【讨论】:

不完全。由于在 CPython 中使用 C 线程,因此从技术上讲,多个线程仍然可以运行,但一次只允许一个线程运行 Python 代码。与您描述的差异如此微妙。使用 CPython 的 C 扩展,如果他们需要操作的数据不需要 Python 数据对象的 Python 全局解释器锁,那么多个线程可以愉快地同时运行。【参考方案2】:

让我为@HKTonyLee 的回答加几分。

所以 Python 有这个 GIL。但它在执行例如文件 I/O 时被释放。这意味着您可以并行读取文件。由于从进程的角度来看,没有文件之类的东西,只有文件描述符(假设为 posix),所以无论您读取什么,它都不必存储在磁盘上。

总而言之,如果您将文件移动到(例如)tmpfs 或 ramdisk 或任何等效设备,那么您应该获得比 SSD 更好的性能。但请注意风险:如果您需要修改文件,您可能会丢失更新。

【讨论】:

可悲的是,GIL 非常糟糕,处理 CPU 处理的线程越多,例如 UDP 套接字上的 IO 读取速度就越慢,并且它会开始丢弃数据包......

以上是关于python多线程读取只读内存缓冲区绕过GIL的主要内容,如果未能解决你的问题,请参考以下文章

读取缓冲区拥有的访问内存

java内存映射文件多线程读/写

绕过内存错误以在 Python 中读取大型 JSON 文件 [关闭]

我可以在没有任何锁的情况下从不同的线程读取内存缓冲区吗?

java 多线程-volatile写后立即读

多线程:多线程设计模式:生产者-消费模式