1. 程式人生 > >『Python CoolBook』C擴展庫_其六_線程

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

正在 args adsi 控制 數組 代碼 and begin extend

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。

C和Python中的線程混用

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

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

#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管理,應該沒什麽問題。

要註意的是調用 PyGILState_Ensure() 並不會立刻搶占或中斷解釋器。 如果有其他代碼正在執行,這個函數被中斷知道那個執行代碼釋放掉GIL。 在內部,解釋器會執行周期性的線程切換,因此如果其他線程在執行, 調用者最終還是可以運行的(盡管可能要先等一會)。

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