在工作中遇到一个导入挂起的问题,花费了很长的时间才解决,
记录一下。
问题简单的复现如下:
a.py:
<span style="font-size:18px;">#coding: utf-8
import datetime
import b</span>
b.py:
#coding: utf-8
import threading
import sys
def test():
print 'before import datetime'
import datetime
print 'after import datetime'
thd = threading.Thread(target=test)
thd.start()
print 'before b.join'
thd.join()
print 'after b.join'
单独执行python b.py是没有任何问题的,但是如果执行python a.py的话,
程序会停在thd.join()这段代码处。如果把test函数中的import datetime给
去掉的话,则python a.py也没有任何的问题。问题就出在import datetime
上。先看一下官网上怎么说的。
While the import machinery is thread-safe, there are two key restrictions
on threaded imports due to inherent limitations in the way that thread-safety
is provided:
1.Firstly, other than in the main module, an import should not have the
side effect of spawning a new thread and then waiting for that thread
in
any way.
Failing to abide
by this restriction can lead to a deadlock if the spawned thread
directly or indirectly attempts to import a module.
我只摘录了导入挂起相关的部分。从官网解释的来看,主要是因为在import的
时候,使用了import mchinery,而且import mchinery是线程安全的。也就是
说,当有一个线程占用了import mchinery就相当于加锁了,如果其他的线程
想import,那么必须获取这个锁才行。
现在回到我们的例子,在我们执行python
a.py
的时候执行到了import b,主线程获取了import mchinery的lock,那么继
续向下执行。
在导入b的过程中,创建了另一线程C并且线程C中import了datetime
所以线程C尝试着去获取import
mchinery
的lock,
而此时import
mchinery的
lock被
主线程拥有,并且还没有释放呢,
所以线程
C只能等import mchinery的
lock被释放,而
此时主线程只有等到线程C结束之后,才能释放import mchinery
的lock,
很显然,这里形成
了死锁。所以导入执行到thd.join()时就因为死锁而挂起了。
解决的方法就是不要在新创建的线程中导入任何模块,把需要导入的模块在一开始就
导入进去。
还有一个值得奇怪的问题的是,为啥直接python b.py没有问题呢,这个也是在新创
建的线程
中import的啊,个人认为主要是因为python b.py的时候,创建线程C的时候
没有任何线程在占用着import mchinery的lock,所以直接执行python b.py的时候,
线程C的导入不会形成死锁。