線程,線程安全與python的GIL鎖


  今天看到一篇文章,講述的是幾個提升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歸咎於安卓系統,阿里中間件等等,是不是舊技術還沒用明白,為了新技術就可以不再去研究。

  還是小菜鳥,在此大談行業發展難免有些放肆,如有不對的地方,還請各位前輩不吝指正

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM