Python怎麼利用多核cpu
利用 ctypes 繞過 GIL
ctypes 與 Python 擴充套件不同,它可以讓 Python 直接呼叫任意的 C 動態庫的匯出函式。你所要做的只是用 ctypes 寫些 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 寫自動化測試用例,通過 ctypes 直接呼叫 C 模組的介面來對這個模組進行黑盒測試,哪怕是有關該模組 C 介面的多執行緒安全方面的測試,ctypes 也一樣能做到。