什么是Python中的导入锁?

2 投票
2 回答
8007 浏览
提问于 2025-04-16 10:21

我正在阅读《信号量小册子》,书中有一些代码可以让Python的语法和书里用的一样。不过,当我尝试导入他的代码时,出现了以下错误。

from threading_cleanup import *
RuntimeError: not holding the import lock

我知道这个错误和监视器函数的代码有关,因为如果我把它注释掉,错误就消失了。那段代码的作用是让我可以用键盘中断来结束程序。

有没有办法解决这个错误呢?

threading_cleanup.py

import threading
import time
import os
import signal
import sys

__all__ = ['Thread', 'Semaphore', 'watcher']

class Thread(threading.Thread):
    def __init__(self, target, *args):
        threading.Thread.__init__(self, target=target, args=args)
        self.start()


class Semaphore(threading._Semaphore):
    wait = threading._Semaphore.acquire

    def signal(self, n=1):
        for _ in range(n): self.release()

    def value(self):
        return self._Semaphore__value


def watcher():
    child = os.fork()
    if child == 0: return
    try:
        os.wait()
    except KeyboardInterrupt:
        print 'KeyboardInterrupt'
        os.kill(child, signal.SIGKILL)
    sys.exit()


watcher()

2 个回答

2

这个问题的标题在问什么是导入锁。

导入锁是Python在处理import时的一部分,它会让程序在你违反一些不太明显的限制时出错。

你可以查看这个链接了解更多信息:https://docs.python.org/2/library/threading.html#importing-in-threaded-code

在你的例子中,因为你直接在模块里调用了watcher(),所以除非这个模块是主模块,否则它不能启动线程。这里有个例子:

python2.7 - 导入时线程静默锁定

不过你的例子似乎有点不同,因为它涉及到进程。如果我把你的threading_cleanup.py简化成:

import os
def watcher():
    child = os.fork()
watcher()

我还是会遇到同样的错误:

  File "main.py", line 1, in <module>
    import threading_cleanup.py
RuntimeError: not holding the import lock

所以这可不是导入锁。虽然错误信息说这是导入锁,对吧?这听起来像是错误信息的一个bug。

1

在我的设置中,这个错误只在解释模式下出现。

看起来解释器不喜欢在还在导入模块的时候就进行分叉操作。

如果你去掉 watcher() 的调用,或者把它放在 if __name__ == '__main__': 这个条件语句里,错误就会消失。

一般来说,Python模块中执行的代码应该只用于初始化全局变量和单例对象。

哦!在导入之后,你可以从解释器中调用 threading_cleanup.watcher(),这样就不会抛出异常了。

呃!我意识到我没有回答你问题的标题:

调用 fork() 会创建一个新的解释器进程;这个新进程需要导入模块才能开始执行。在解释模式下,你是在模块还在导入的过程中就让这个操作发生了,因此会出现锁定的情况。在解释模式下,交互式解释器是主程序。而在执行模式下,比如用 python mymodule.py,模块就是主程序,所以它不会被 导入。这样说你明白了吗?

撰写回答