Python腳本會交給解釋器的GIL機制進行調度,不管CPU有多少個核心,在同一時刻,只有一個線程片段能在解釋器中運行。就是說腳本是同步(串行)運行的,除非遇到阻塞,比如I/O作業,此時其他線程會搶到GIL調用CPU等運行非阻塞任務。
Python的多線程意味着可以讓多個任務交替運行,這樣能夠避免執行阻塞任務時,非阻塞任務被阻塞,比如白白讓CPU出現大量空閑。
任務交替運行,特別要注意加鎖,需要保護特定數據在修改過程中的原子性。Python在改變數據時,過程是漫長的,包括取句柄、讀對象、創建對象、賦值到句柄等。但是這個過程是不是上述的I/O作業呢(它在內存中運行)?不知道,但確實是阻塞的。
使用數據庫本身進行連接,是真正的建立連接。使用線程池,包括SQLAlchemy的 engine = sqlalchemy.create_engine('...///path'),實際上是建立線程池:conn = engine.connect()是取出一個連接,conn.close()是返還這個連接。
使用線程池,“每次取一個連接---返還”,並非總是非常高效,在並發很少或沒有並發的時候,趕不上使用“每次直接連接---關閉”。
而在Python多線程中,當不是借由Web服務器並發的場景時,使用連接池效率很低。
一個連接,只能由一個線程使用,多個線程無法使用同一個連接,至少sqlite3是這樣。這種情況可能是因為一個連接“正忙”,無法被其他線程使用。或者引擎不判斷忙碌和空閑,或者引擎的設計感到沒必要判斷。結果就一個線程只能獨享一個連接。
如果多線程使用線程池超出了限額,線程池也不再新增線程,按上面的推斷,程序會報錯。
使用連接池效率低的原因可能是線程數量超出線程池限額,需要等待;而使用直連時可以不斷創建新的連接。如果不想再猜測,可以細細研究 sqlalchemy.create_engine()關於連接池的參數說明。
**********
以上純粹是個人揣摩。