【转】python 线程,GIL 和 ctypes
转自:http://zhuoqiang.me/a/python-thread-gil-and-ctypes
4 利用 ctypes 绕过 GIL
ctypes 与 python 扩展不同,它可以让 Python 直接调用任意的 C 动态库的导出函数。你所要做的只是用 ctypes 这个 Python 库写些 python 代码即可。最酷的是,ctypes 会在调用 C 的函数前释放 GIL。所以,我们可以通过 ctypes 和 C 动态库来让 python 充分利用物理内核的计算能力。让我们来实际验证一下,我们用 C 写一个死循环函数
extern"C" { void DeadLoop() { while (true); } } |
用上面的 C 代码编译生成动态库 libdead_loop.so,接着就要利用 ctypes 来在 python 里 load 这个动态库,分别在主线程和新建线程里调用其中的 DeadLoop
from ctypes import * from threading import Thread lib = cdll.LoadLibrary("libdead_loop.so") t = Thread(target=lib.DeadLoop) t.start() lib.DeadLoop() |
这回再看看 system monitor,Python 解释器进程有两个线程在跑,而且双核 CPU 全被占满了!ctypes 确实很给力。需要提醒的是,GIL 是被 ctypes 在调用 C 的函数前释放了。但是 Python 解释器还是会在执行任意一段 Python 代码时锁 GIL 的。如果你使用 Python 的代码做为 C 函数的 callback,那么只要 Python 的 callback 方法被执行时,GIL 还是会跳出来的。比如下面的例子:
extern"C" { typedef void Callback(); void Call(Callback* callback) { callback(); } } |
from ctypes import * from threading import Thread def dead_loop(): while True: pass lib = cdll.LoadLibrary("libcall.so") Callback = CFUNCTYPE(None) callback = Callback(dead_loop) t = Thread(target=lib.Call, args=(callback,)) t.start() lib.Call(callback) |
注意这里与上个例子的不同之处,这次的死循环是发生在 Python 代码里 (DeadLoop 函数) 而 C 代码只是负责去调用这个 callback 而已。运行这个例子,你会发现 CPU 占用率还是只有 50% 不到。GIL 又起作用了。
其实,从上面的例子,我们还能看出 ctypes 的一个应用,那就是用 python 写测试 case,通过 ctypes 直接调用 C 模块的接口来对这个模块进行黑盒测试,哪怕是有关该模块 C 接口的多线程安全方面的测试,ctypes 也一样能做到。
浙公网安备 33010602011771号