python threading2種調用方式實例


1.認識GIL:

說到GIL一直是代碼專家們一直以來想要解決的問題,也是被許多程序員詬病的,下面帶領大家看下官方threading模塊document中如何去描述對於GIL這個全局解釋器鎖的:https://docs.python.org/3/library/threading.html

全局解釋器鎖

所使用的機制的CPython解釋器來確保只有一個線程執行的Python 字節碼在一個時間。通過使對象模型(包括關鍵的內置類型,例如dict)隱式安全地防止並發訪問,從而簡化了CPython的實現鎖定整個解釋器可以使解釋器更容易進行多線程處理,但會犧牲多處理器機器提供的許多並行性。

但是,某些擴展模塊(標准的或第三方的)被設計為在執行諸如壓縮或散列之類的計算密集型任務時釋放GIL。另外,在執行I / O時,始終釋放GIL。

過去創建“自由線程”解釋器(一種以更精細的粒度鎖定共享數據的解釋器)的努力並未成功,因為在常見的單處理器情況下性能會受到影響。相信克服該性能問題將使實施更加復雜,因此維護成本更高。

 

 

 

在這里專門對全局解釋器鎖的概念引入時說了這么一段話,第一指明了GIL造成了python解釋器一次只有一個線程可以獲取解釋器鎖,也就是解釋器本身是自帶鎖的,誰先拿到誰就可以搶占到cpu的資源,

從而導致了python無論一個線程怎么去跑,都只能最多跑滿一個cpu核心,就造成了多核cpu資源的無法利用的帶來的資源浪費,但是這是否就意味着python一無是處呢,答案肯定No!,雖然GIL對於cpu密集的支持

不是那么的友好,但是,對於IO密集的支持恰恰是其他語言所無法比擬的,就拿asyncio這這個協程庫來講:https://docs.python.org/3/library/asyncio.html

1.第一部分認識多線程創建以及調用,上一節我講解了源碼關於多線程實現方式有兩種:第一種通過Thread類傳入可調用target對象,第二種繼承Thread類並重寫run方法:

下面就以target進行舉例:

 

 

 

 可以看到線程的資源搶占效果;證明GIL鎖的的確真實存在的;

 

先在我們如果更換t.join()到t1.start()的后面會發現,線程每次不管怎么運行都是先執行完threading1線程,再去執行threading2線程,而這完全得益於joIn()函數:

 

 

我們來看源碼是怎么解釋join()的:

等待直到線程終止。這將阻塞調用線程,直到join()被調用方法的線程終止(正常或通過未處理的異常終止),或者直到發生可選的超時。

 

 講完join():

我們再來認識一個有趣的參數daemon->守護線程:

先看源碼:

 

 daemon用'來指示線程是否是守護線程,必須在線程start()方法調用之前設置,否則會引發RunTimeError,當不存在活動的非守護線程時將退出python程序

演示daemon:

正常默認daemon為False:

 

 可以看到主線程運行結束后退出后子線程繼續運行並輸出了內容,

但是現在如果有個需求要求我們實現主線程退出后必須kill掉子線程那么,如何實現這個需求,這就用到了daemon,我們只需要設置為True:

 

 主線程運行結束后並沒有等待子線程

運行完畢就kill掉了子線程沒有輸出finsh打印就已經結束了子線程了

 

 

# 多線程實現方式二,繼承Thread 類,重寫run方法:

 

 這是一種很基本的寫法。實際開發過程我們會涉及到類的調用以及類的方法引用寫法會發生改變:

 

 

這里實現了在線程外部定義一個類,其中類的方法test作為一個可調用對象傳給了繼承了Thread並重寫run方法的MyCallable類,這里留一個思考題,對於以上重寫run你會發現我們的run是無法實現結果回調的

這就意味着,我們需要根據實際場景使用不同的模塊處理不同的需求,可以給大家一個小提示可以使用concurrent.futures或者使用  queue.Queue()來實現結果回調,因為在queue源碼中,它是基於deque來實現的而deque是線程安全的也就意味queue也是線程安全的,有興趣的可以自己去看下

j結語:

 

 

 


免責聲明!

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



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