我有一些使用__import__
我最近偶然发现了一些关于Python中“导入锁定”的文章,即专门针对导入的锁(不仅仅是GIL)。但这篇文章很旧,所以也许这不再是真的了。
这让我对在线程中导入的做法感到疑惑。
import
/ __import__
线程是否安全?2012年9月12日编辑
感谢Soravux的出色回复。
因此导入是线程安全的,我并不担心死锁,因为在我的代码中使用__import__
的函数不会互相调用。
您是否知道即使模块已经导入也是否获得锁定?
如果是这种情况,我应该查看sys.modules,以便在调用__import__
之前检查模块是否已经导入。
当然,这不应该在CPython中产生很大的不同,因为无论如何都有GIL。 但是它可能会在其他实现上产生很大的不同,比如Jython或无栈python。
2012年9月19日编辑
关于Jython,这是他们在doc中所说的内容:
http://www.jython.org/jythonbook/en/1.0/Concurrency.html#module-import-lock
但是,Python确实定义了一个模块导入锁,即
由Jython实现。只要导入任何内容,就会获得此锁定
名字是。无论导入是通过导入,都是如此
声明,等效的__import__
内置或相关代码。它的
重要的是要注意即使相应的模块已经存在
如果已导入,则仍将获取模块导入锁定
简要地
因此,在进行导入之前检查sys.modules似乎是有意义的,以避免获取锁定。你觉得怎么样?
答案 0 :(得分:11)
正常导入是线程安全的,因为它们在执行之前获取导入锁定,并在导入完成后释放它。如果使用可用的挂钩添加自己的自定义导入,请务必将此锁定方案添加到其中。 imp
模块(imp.lock_held()
/ acquire_lock()
/ release_lock()
)可以访问Python中的锁定工具。
使用此导入锁不会创建除already known的循环依赖项之外的任何死锁或依赖项错误。
在Linux上创建一个线程为clone
的低级调用,在Python中进行线程化是一个类似fork的操作。分叉和克隆在各种内存段上应用不同的行为。例如,与克隆更多段(数据(通常是COW),堆栈,代码,堆)的分支相比,只有堆栈不被线程共享,实际上不共享其内容。 Python中的导入机制使用位于堆栈上的 not 的全局命名空间,因此使用带有其线程的共享段。由于导入的副作用(即内存更改)在相同的段中工作,因此它表现为单线程程序。但是,请注意在多线程程序的导入中使用线程安全库。 将导致混乱使用对此类环境中非线程安全的函数的调用。
顺便说一句,Python中的线程程序会遇到GIL,除非你的程序受I / O限制或依赖于C或外部线程安全库(因为它们发布了GIL),否则它不会带来太多的性能提升在执行之前)。由于此GIL,在两个线程中运行相同的导入函数将不会同时执行。请注意,这只是CPython的限制,而Python的其他实现会有不同的行为。
回答您的编辑:导入的模块都由Python缓存。如果模块已经加载到缓存中,它将不再运行,并且import语句(或函数)将立即返回。您不必在sys.modules中实现缓存查找,Python会为您执行此操作,并且除了用于sys.modules查找的GIL之外,不会imp
锁定任何内容。
回答你的第二次编辑:我更喜欢维护一个比试图优化对我使用的库(在本例中为标准库)的调用更简单的代码。基本原理是执行某些操作所需的时间通常比导入执行该操作的模块所需的时间更重要。此外,在整个项目中维护此类代码所需的时间远高于执行所需的时间。这一切归结为:“程序员时间比CPU时间更有价值”。
答案 1 :(得分:0)
我在官方文档中没有找到答案,但是似乎在某些版本的CPython 3.x中,__import__
调用不是线程安全的,并且可能导致死锁。参见:https://bugs.python.org/issue38884。