本文的文字及圖片來源于網路,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理
以下文章來源于Python知識學堂 ,作者: 東不歸
私信回復“資料”,即可免費領取Python實戰案例講解視頻
Python多執行緒爬蟲講解視頻觀看地址
https://www.bilibili.com/video/BV1L54y1r73F/
前言
本次推文介紹一下多執行緒,不過值得注意的是,不能濫用多執行緒,多執行緒爬蟲請求內容速度過快,可能會導致服務器過載,或者是IP被封禁,為了避免這一問題,我們在使用多執行緒爬蟲的時候需要設定一個delay時間,用于請求同一域名時的最小時間間隔,
執行緒和行程如何作業
當程式在運行時,就會創建包含代碼和狀態的行程,這些行程通過一個或者多個CPU來執行,不過同一時刻每個CPU只會執行一個行程,然后在不同行程之間快速切換,這樣就感覺多個程式同時運行,同理,在一個行程中,程式的執行也是在不同執行緒間進行切換的,每個執行緒執行程式的不同部分,這就意味著一個執行緒在等待執行時,行程會切換到其他的執行緒執行,這樣可以避免浪費CPU時間,
Threading執行緒模塊
在Python標準庫中,使用threading模塊來支持多執行緒,Threading模塊對thread進行了封裝,絕大數情況,只需要使用threading這個模塊,使用起來也非常簡單:
t1=threading.Thread(target=run,args=("t1",)) 創建一個執行緒實體 # target是要執行的函式名(不是函式),args是函式對應的引數,以元組的形式存在 t1.start() 啟動這個執行緒實體,
普通創建方式
執行緒的創建很簡單,如下:
import threading import time def printStr(name): print(name+"-python青燈") s=0.5 time.sleep(s) print(name+"-python青燈") t1=threading.Thread(target=printStr,args=("你好!",)) t2=threading.Thread(target=printStr,args=("歡迎你!",)) t1.start() t2.start()
自定義執行緒
本質是繼承threading.Thread,重構Thread類中的run方法
import threading import time class testThread(threading.Thread): def __init__(self,s): super(testThread,self).__init__() self.s=s def run(self): print(self.s+"——python") time.sleep(0.5) print(self.s+"——青燈") if __name__=='__main__': t1=testThread("測驗1") t2=testThread("測驗2") t1.start() t2.start()
守護執行緒
使用setDaemon(True)把子執行緒都變成主執行緒的守護執行緒,因此當主執行緒結束后,子執行緒也會隨之結束,也就是說,主執行緒不等待其守護執行緒執行完成再去關閉,
import threading import time def run(s): print(s,"python") time.sleep(0.5) print(s,"青燈") if __name__ == "__main__": t=threading.Thread(target=run,args=("你好!",)) t.setDaemon(True) t.start() print("end")
結果:
你好! python
end
當主執行緒結束后,守護執行緒不管有沒有結束,都自動結束,
主執行緒等待子執行緒結束
使用join方法,讓主執行緒等待子執行緒執行,如下:
import threading import time def run(s): print(s,"python") time.sleep(0.5) print(s,"青燈") if __name__ == "__main__": t=threading.Thread(target=run,args=("你好!",)) t.setDaemon(True) t.start() t.join() print("end")
結果:
你好! python
你好! 青燈
end
以上是多執行緒的幾種簡單的用法,那么threading模塊還有做什么呢?請往下看,
Lock 鎖
其實在介紹diskcache快取的時候也介紹過鎖的相關內容,其實不難理解為啥多執行緒中也會出現鎖的概念,當沒有保護共享資源時,多個執行緒在處理同一資源時,可能會出現臟資料,造成不可以預期的結果,即執行緒不安全,
如下示例出現不可預期的結果:
import threading price=0 def changePrice(n): global price price=price+n price=price-n def runChange(n): for i in range(2000000): changePrice(n) if __name__ == "__main__": t1=threading.Thread(target=runChange,args=(5,)) t2=threading.Thread(target=runChange,args=(8,)) t1.start() t2.start() t1.join() t2.join() print(price)
理論上的結果為0,但是每次運行的結果可能都是不一樣的,
所以這個時候就需要鎖去處理了,如下:
import threading import time from threading import Lock price=0 def changePrice(n): global price lock.acquire() #獲取鎖 price=price+n print("price:"+str(price)) price=price-n lock.release() #釋放鎖 def runChange(n): for i in range(2000000): changePrice(n) if __name__ == "__main__": lock=Lock() t1=threading.Thread(target=runChange,args=(5,)) t2=threading.Thread(target=runChange,args=(8,)) t1.start() t2.start() t1.join() t2.join() print(price)
結果值與理論值是一致的,鎖的意義在于每次只允許一個執行緒去修改同一資料,以保證執行緒安全,
信號量
BoundedSemaphore類,同時允許一定數量的執行緒更改資料,如下:
import threading import time def work(n): semaphore.acquire() print("序號:"+str(n)) time.sleep(1) semaphore.release() if __name__ == "__main__": semaphore=threading.BoundedSemaphore(5) for i in range(100): t=threading.Thread(target=work,args=(i+1,)) t.start() #active_count獲取當前正在運行的執行緒數 while threading.active_count()!=1: pass else: print("end")
結果為:每5次列印停頓一下,直到結束,
GIL全域解釋器鎖
說到多執行緒,不得不提一下GIL,GIL的全稱是Global Interpreter Lock(全域解釋器鎖),這是python設計之初,為了資料安全所做的決定,某個執行緒想要執行,必須先拿到GIL,并且在一個行程中,GIL只有一個,只有拿到GIL的執行緒,才能進入CPU執行,GIL只在cpython中才有,因為cpython呼叫的是c語言的原生執行緒,所以他不能直接操作cpu,只能利用GIL保證同一時間只能有一個執行緒拿到資料,而在pypy和jpython中是沒有GIL的,
總結
本篇文章介紹了多執行緒的用法,要根據實際的情況去使用,多執行緒編程,容易發生沖突,必須用鎖加以隔離,又得小心發生死鎖,由于Python的設計時有GIL全域鎖,導致多執行緒無法利用多核,使得在多執行緒并發的情況下并不理想,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/238889.html
標籤:Python
上一篇:python aes加密
下一篇:Common encryption methods and implementation in Python Python中常用的加密方法及實作
