今天看到一篇文章,講述的是幾個提升python性能的項目:傳送門
在看的過程中,接觸到一個名詞,一個從學python開始就一直看到,但是從來都是一知半解的名詞,心里不開心,必須把它搞明白,對了,這個詞就是 GIL。網上搜索了一些資料,粗淺的理解了什么是GIL,自己感覺學習的過程比較好,感覺略有收獲,老規矩,為了鞏固知識,自己整片文章出來寫一寫,其實好多文章已經寫的很完善了,所以這篇隨筆,只做知識鞏固,如有雷同,請各位原創作者原諒,小菜鳥一枚,如果哪里寫的有問題,還請各位前輩不吝指正。
一句話:解決多線程之間數據完整性和狀態同步的最簡單方法自然就是加鎖。
首先,GIL的全名,Global Interpreter Lock
,鑒於英文水平,不做名詞翻譯,以免誤導。大體解釋一下,這個鎖就是用來為了解決Cpython多線程中線程不安全問題引入的一個全局排它鎖,它的作用就是在多線程情況下,保護共享資源,為了不讓多個線程同時操作共享資源,導致不可預期的結果而加上的鎖,在一個線程操作共享資源時,其他線程請求該資源,只能等待GIL解鎖。這個設置在Cpython剛引入多線程概念的時候就有了,然后后續的各種包和組件開發都不可避免的受到了GIL的影響,所以有人會說,python在多線程處理的時候很慢。python GIL實現方式類似於如下偽代碼:
if __name__ == '__main__': GIL鎖開始運作 主線程做操作 主線程完成操作 GIL鎖釋放資源
所以多線程共同操作共享資源的時候,有一個線程競得了資源,它就被GIL鎖保護起來,其他線程只能是在那里等着,但是這個時候,線程的休眠喚醒,全部會消耗CPU資源,所以嘞,就會慢。
看到這個時候,我又發現了一個名詞:線程安全。這個名詞,也是那種特別熟悉,但就是無法清晰的說出它是啥的概念。查了資料,在這記一下:
線程安全就是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現數據不一致或者數據污染。 線程不安全就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據。
我自己想了一下,大約就是這樣,比如整個列表,倆個線程同時在列表中append操作,如果沒有鎖的保護,在機緣巧合之下,倆個線程同時先后申請了空間且沒來得及插入數據,然后這時列表中只會有一個空間,那么在插入過程中只能有一個數據寫入,會造成不可知后果,有可能報錯終止,有可能有一個線程操作沒成功,那么這個就是線程不安全了,大白話說,只要線程之間沒有共享資源,那么就是線程安全的,有共享資源,為了保證線程安全,需要引進鎖的機制。
而后的文章中,有前輩做過實驗:
順序執行的單線程(single_thread.py) #! /usr/bin/python from threading import Thread import time def my_counter(): i = 0 for _ in range(100000000): i = i + 1 return True def main(): thread_array = {} start_time = time.time() for tid in range(2): t = Thread(target=my_counter) t.start() t.join() end_time = time.time() print("Total time: {}".format(end_time - start_time)) if __name__ == '__main__': main()
同時執行的兩個並發線程(multi_thread.py) #! /usr/bin/python from threading import Thread import time def my_counter(): i = 0 for _ in range(100000000): i = i + 1 return True def main(): thread_array = {} start_time = time.time() for tid in range(2): t = Thread(target=my_counter) t.start() thread_array[tid] = t for i in range(2): thread_array[i].join() end_time = time.time() print("Total time: {}".format(end_time - start_time)) if __name__ == '__main__': main()
最終結果如下:
以上測試代碼和圖片引用自:
http://cenalulu.github.io/python/gil-in-python/
過程證明了因為GIL的存在,導致python在使用多線程的時候反而不如順序執行快。
此處我又溫習了一下python線程:
線程的順序執行還是多線程並發,取決於join函數的位置。join函數的作用是等待當前線程結束,所以每一個線程創建之后,調用start函數,這是在后面跟上該線程的join函數,那么就是順序執行,如果多個線程先完成創建和start,最后加上join函數,那么就變成了多線程並發。
這就是今天的學習內容,其實所有知識網上都能找到,更想分享的是一種學習的方法,一種本身很不推薦的學習方法,那就是類似於探索性測試的學習,啥不懂就去看啥,有些時候,我們學習東西確實不能非要究其內在,軟件行業的學習本身在非本行人事看來就特別神奇且枯燥,所以最初的學習,我們需要整個圖形界面,讓我們學到的東西有了成就感,如果上來先去研究機器碼,那么沒幾個人願意學下去,但是不管怎樣,既然走上了軟件行業的道路,這種探索性,打破砂鍋問到底的學習,在我的感覺里應該是必經之路,也就是所謂的底層研究。以安卓開發舉例,如果做安卓開發的,雖然能寫出很漂亮的界面,解決所有的bug,如果不了解安卓系統linux層的知識,在我的眼里,從未把這種研發看做大牛。當然我並不覺得不了解linux底層的安卓研發可以解決任何bug
當下的軟件行業進入了一個神奇的階段,我已經聽過無數遍的理論,培訓機構出來就能賺錢,大學讀着沒用,在這里不討論教育體制問題,從個人情感上,我覺得大學教育雖然沒有教給學生直接找工作的技能,但是給了所有學生一個能夠了解基礎知識的園地,換而言之,作為行業的一員,總應該有將行業發展起來的覺悟,行業內整體風氣,缺乏靜下心來的沉淀。在大談敏捷,行為驅動,機器學習的同時,自己需要靜下心來回頭看看,基礎已然不牢,再走下去是否有些危險。是不是學習軟件技術,就是為了獲取互聯網行業那虛高的工資,是否已經局限於第三方框架,一旦框架出現問題,只能打給客服而束手無策,是否有過沒有做任何嘗試就將bug歸咎於安卓系統,阿里中間件等等,是不是舊技術還沒用明白,為了新技術就可以不再去研究。
還是小菜鳥,在此大談行業發展難免有些放肆,如有不對的地方,還請各位前輩不吝指正