导入Python线程内部

Joh*_*nal 14 python concurrency multithreading locking python-import

我有一些使用交互式加载python模块的函数 __import__

我最近偶然发现了一篇关于Python中"导入锁定"的文章,即专门针对导入的锁(不仅仅是GIL).但文章很旧,所以也许这不再是真的了.

这让我想知道在线程中导入的做法.

  1. import/ __import__线程安全的?
  2. 他们可以创造死锁吗?
  3. 它们是否会在线程应用程序中导致性能问题?

编辑2012年9月12日

感谢Soravux的出色回复.因此导入是线程安全的,我不担心死锁,因为__import__我的代码中使用的函数不会相互调用.

您是否知道即使已经导入模块也会获得锁定?如果是这种情况,我应该查看sys.modules,以便在调用之前检查模块是否已经导入__import__.

当然,这不应该在CPython中产生很大的不同,因为无论如何都有GIL.但是它可能会在其他实现上产生很大的不同,比如Jython或stackless python.

编辑2012年9月19日

关于Jython,这是他们在文档中所说的:

http://www.jython.org/jythonbook/en/1.0/Concurrency.html#module-import-lock

但是,Python确实定义了一个模块导入锁,它由Jython实现.只要输入任何名称,就会获得此锁定.无论导入是通过import语句,等效__import__内置函数还是相关代码,都是如此.重要的是要注意,即使已导入相应的模块,仍将获取模块导入锁定(如果只是短暂的).

因此,在进行导入之前检查sys.modules似乎是有意义的,以避免获取锁定.你怎么看?

Sor*_*vux 13

正常导入是线程安全的,因为它们在执行之前获取导入锁定,并在导入完成后释放它.如果使用可用的挂钩添加自己的自定义导入,请务必将此锁定方案添加到其中.imp模块(imp.lock_held()/ acquire_lock()/ release_lock())可以访问Python中的锁定工具.

除了已知的循环依赖项之外,使用此导入锁定不会创建任何死锁或依赖性错误.

clone在Linux上创建线程的低级调用,在Python中进行线程化是一个类似fork的操作.分叉和克隆在各种内存段上应用不同的行为.例如,与克隆更多段(数据(通常是COW),堆栈,代码,堆)的分支相比,只有堆栈不被线程共享,实际上不共享其内容.Python中的导入机制使用放置在堆栈上的全局命名空间,因此使用带有其线程的共享段.由于导入的副作用(即内存更改)在相同的段中工作,因此它表现为单线程程序.但是,请注意在多线程程序的导入中使用线程安全库.它导致混乱使用对这种环境中非线程安全的函数的调用.

顺便说一下,Python中的线程程序会受到GIL的影响,除非你的程序受I/O限制或依赖于C或外部线程安全库(因为它们在执行之前释放GIL),否则它不会带来太多的性能提升.由于此GIL,在两个线程中运行相同的导入函数将不会同时执行.请注意,这只是CPython的限制,而Python的其他实现将具有不同的行为.

要回答您的编辑:导入的模块都由Python缓存.如果模块已经加载到缓存中,它将不再运行,并且import语句(或函数)将立即返回.您不必在sys.modules中实现缓存查找,Python会为您执行此操作imp,除了用于sys.modules查找的GIL之外,不会锁定任何内容.

回答你的第二次编辑:我更喜欢维护一个比试图优化对我使用的库(在本例中为标准库)的调用更简单的代码.基本原理是执行某些操作所需的时间通常比导入执行该操作的模块所需的时间更重要.此外,在整个项目中维护此类代码所需的时间远高于执行所需的时间.这一切归结为:"程序员时间比CPU时间更有价值".