『Python CoolBook』C擴展庫_其六_線程

GIL操做

想讓C擴展代碼和Python解釋器中的其餘進程一塊兒正確的執行, 那麼你就須要去釋放並從新獲取全局解釋器鎖(GIL)。數組

在Python接口封裝中去釋放並從新獲取全局解釋器鎖(GIL),此時本段程序失去GIL運行,其餘線程能夠無視本函數的運行而運行,直到Py_END_ALLOW_THREADS:安全

#include "Python.h"
...

PyObject *pyfunc(PyObject *self, PyObject *args) {
   ...
   Py_BEGIN_ALLOW_THREADS
   // Threaded C code.  Must not use Python API functions
   ...
   Py_END_ALLOW_THREADS
   ...
   return result;
}

只有當你確保沒有Python C API函數在C中執行的時候你才能安全的釋放GIL。 GIL須要被釋放的常見的場景是在計算密集型代碼中須要在C數組上執行計算(好比在numpy中) 或者是要執行阻塞的I/O操做時(好比在一個文件描述符上讀取或寫入時)。函數

當GIL被釋放後,其餘Python線程才被容許在解釋器中執行。 Py_END_ALLOW_THREADS 宏會阻塞執行直到調用線程從新獲取了GIL。spa

C和Python中的線程混用

混合使用C、Python和線程, 有些線程是在C中建立的,超出了Python解釋器的控制範圍, 而且一些線程還使用了Python C API中的函數時,須要確保正確的初始化和管理Python的全局解釋器鎖(GIL)。線程

要想這樣,能夠將下列代碼放到你的C代碼中並確保它在任何線程被建立以前被調用:code

#include <Python.h>
  ...
  if (!PyEval_ThreadsInitialized()) {
    PyEval_InitThreads();
  }
  ...

對於任何調用Python對象或Python C API的C代碼,確保你首先已經正確地獲取和釋放了GIL, 這能夠用 PyGILState_Ensure()PyGILState_Release() 來作到,以下所示:對象

...
/* Make sure we own the GIL */
PyGILState_STATE state = PyGILState_Ensure();

/* Use functions in the interpreter */
...
/* Restore previous GIL state and return */
PyGILState_Release(state);
...

在涉及到C和Python的高級程序中,不少事情一塊兒作是很常見的—— 多是對C、Python、C線程、Python線程的混合使用。 只要你確保解釋器被正確的初始化,而且涉及到解釋器的C代碼執行了正確的GIL管理,應該沒什麼問題。blog

要注意的是調用 PyGILState_Ensure() 並不會馬上搶佔或中斷解釋器。 若是有其餘代碼正在執行,這個函數被中斷知道那個執行代碼釋放掉GIL。 在內部,解釋器會執行週期性的線程切換,所以若是其餘線程在執行, 調用者最終仍是能夠運行的(儘管可能要先等一會)。接口

相關文章
相關標籤/搜索