Python 有GIL保證相關對象的同時操作,那為什麼還需要進行線程同步?是不是因為在虛擬機器層面之上在多線程工作時還需要提供相關的鎖機制、隊列機制,這其中的原理是什嗎?有其他類似的例子嗎? 還是說Python內部的GIL保證了一個變數的引用數量,但是當多線程使用同一資源時,例如sys.stdout,這個時候是需要線程同步來確保資源訪問不衝突,GIL不提供類似資源的保護嗎?
回複內容:
GIL也只是相當於古時候單核CPU通過不斷的分配時間片來類比多線程的方法而已,為什麼那個時候寫多線程也要用鎖?這在網上都說爛了. 不過我當初也思考了這個問題一會兒.
你自己想想看這個情景
線程AB同時操作list
list的[0]初始值為0
線程A 操作100次
list[0]+=1
線程B 操作100次
list[0]+=1
線上程A 對於 list[0]進行操作時
list[0]為0, 還沒等線程A完成加一操作, 就被切換到線程B了
線上程B 眼裡,
list[0]還是為0, 於是執行加一操作.
再切換回線程A, 繼續未完成的加一操作
你發現了沒!!! 線程AB各對list[0]進行了加一,預期結果是2 但結果還是1
Python的list不是完全安全執行緒的.
所以加個線程鎖就完了.
話說為什麼, 有了線程鎖還需要GIL呢?
因為, 有了GIL, 提供並發就變得很容易. 解譯器只要計算每個線程的已耗用時間就好了
時間一到, 將這個線程凍結, 記憶體管理很簡單.
等等, 你還是沒解釋, 如果我已經給線程上了鎖, 為什麼還是要被GIL限制?
一向符合人類直覺的Python, 有個很反直覺的機制.
Py的變數a其實不是C系編譯語言的變數.
Python維護著一個字典, 儲存著a和對應數值的指標.
用某黑Python的大牛的話說, Python企圖用字典裝下世界..
如果變成真多線程
對於這個字典的維護將會很複雜.
多個線程真正同時操作一個字典, Python引以為傲的字典效能, 估計就沒那麼強了.
就是說, Python字典的效能強大,是建立線上程不安全的基礎上.
而字典在Python中的位置又是如此重要, 一個緩慢的字典, 會嚴重拖慢Python的解釋速度.
多線程操作多個獨立字典. 那樣還是要同步.
那為什麼不採用多進程呢? 這就是社區主流的看法.
雖然理論上線程成本更低, 但是那樣代碼就改成面目全非了..GIL 的作用是:對於一個解譯器,只能有一個thread在執行bytecode。所以每時每刻只有一條bytecode在被執行一個thread。GIL保證了bytecode 這層面上是thread safe的。
但是如果你有個操作比如 x += 1,這個操作需要多個bytecodes操作,在執行這個操作的多條bytecodes期間的時候可能中途就換thread了,這樣就出現了data races的情況了。
比如這小傢伙就有很多條bytecodes:
>>> dis.dis(lambda x: x+1) 1 0 LOAD_FAST 0 (x) 3 LOAD_CONST 1 (1) 6 BINARY_ADD 7 RETURN_VALUE
https://www.youtube.com/watch?v=ph374fJqFPE
gil控制的是位元組碼, 鎖控制的是python代碼,粒度不一樣吧? 比如用鎖控制的代碼被編譯成101條位元組碼 那麼線程在執行這101條位元組碼的時候,cpu會被其他線程利用(python調度線程預設是100條位元組碼 調度一次)
——《Python源碼剖析》GIL是全域解譯器鎖,GIL保證了在同一時間片下總有一個Python(CPython實現)線程在執行。所以即使是多進程,而是順序執行的。這樣多線程並發就變得沒有意義。
- 線程在GIL下是有執行的時間片的
- 在時間片內線程如果沒有成功對資料進行操作,那麼等到下一個時間片時,資料已經被別的線程修改了,那麼得到的資料就不是想要的資料了
- 線程的同步和互斥解決的是線程間資料的訪問正確性問題,而GIL是實現當前Python解譯器下只有一個線程在執行。兩個是不同的概念。