GIL (Global Interpreter Lock) 全局解釋器鎖


通常,Python 采用 PIL 解釋器鎖將應用程序進程,鎖定於一物理綫程基礎之上。

即使是多綫程 CPU,一個應用程序進程同時最多也隻能使用一個物理綫程。

軟件層麵可以實現多綫程,但 Python 仍基於一物理綫程。

要真正實現利用多物理綫程,軟件層麵可以使用 多進程 替換多綫程。

GIL 全局解釋器鎖


Python 解釋器的實現有很多,譬如 C 實現的 CPython、Java 實現的 Jython、Python 實現的 PyPy;其中使用最廣泛的是 CPython。

由於采用 C 實現 Python 解釋器需要處理復雜綫程安全和並發環境內存管理,為降低解釋器實現的復雜性,CPython 引入瞭 GIL (Global Interpreter Lock)。

當使用多綫程時,CPython 解釋器會創建 GIL,每個綫程在執行前會先獲取 GIL,阻止其它綫程的執行。

在下列情況中,正在運行的 Python 綫程會釋放自己的 GIL:

  1. 搶占機製
  2. CPU 竟爭機製:Unix 係統使用時間片算法,微軟 Windows 係統使用搶占式算法。

    CPython 解釋器會定期檢查 GIL 占用時間,若超過瞭某個閾值,會強製綫程釋放 GIL。

    可以采用 sys.setswitchinterval() 設置時間間隔,采用 sys.getswitchinterval() 查看最小時間間隔。

  3. 主動釋放
  4. CPython 綫程等待 I/O 時,會主動釋放 GIL。

GIL 是為方便 CPython 解釋器的實現而産生的,保證字節碼在執行過程中不會被打斷,但並不能保證 Python 程序是綫程安全的。譬如:

>>> def add_one(n):
    n += 1
 
>>> import dis
 
>>> dis.dis(add_one)
  2           0 LOAD_FAST                0 (n)
              2 LOAD_CONST               1 (1)
              4 INPLACE_ADD
              6 STORE_FAST               0 (n)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>>
					

以上範例中的 2 是行號。

函數 add_one 中的 += 從代碼層麵來看是一條語句,但是采用 dis 翻譯成字節碼後,會被分成 INPLACE_ADD 和 STORE_FAST 兩條語句。

由於搶占機製的存在,在執行完 INPLACE_ADD 之後解釋器可能會執行彆的綫程,此時若彆的綫程內也修改瞭 n 的值,就會齣現並發安全性問題。

竟爭機製


Unix 係統使用時間片算法,微軟 Windows 係統使用搶占式算法,調用物理 CPU 綫程。

當一 _thread 在使用某個物理綫程,而另一 _thread 也想使用此物理綫程,就會形成竟爭機製。

若産生的物理綫程竟爭機製時間較長 (3 秒以上) 且當前物理綫程資源被耗盡,就可能導緻應用程序異常、卡死,甚至崩潰。

長期占用


不推薦以同步阻塞 (或 3 秒以下短時間 time.sleep() 休眠) 方式,實現自循環 _thread;因為很容易導緻其它 _thread,與其産生物理綫程竟爭機製。

另請參閱:

Python 如嚮處理 CPU綫程 竟爭機製

版權聲明: 本文為獨傢原創稿件,版權歸 樂數軟件 ,未經許可不得轉載。