多線程
什麼是線程:
線程指的是一條流水線的工做過程的總稱python
線程是CPU的基本執行單位安全
對比進程而言,進程僅僅是一個資源單位其包含了程序運行所需的資源,就像一個車間多線程
而單有資源是沒法生產出產品的,必須有具體的生產產品的邏輯代碼併發
線程就至關於車間中的一條流水線,而你的代碼就是流水線上的一道道工序ui
特色:
1.每一個進程都會有一個默認的線程spa
2.每一個進程能夠存在多個線程操作系統
3.同一進程中的全部線程之間數據是共享的線程
4.建立線程的開銷遠比建立進程小的多code
主線程與子線程的區別:
1.線程之間是沒有父子之分,是平等的對象
2.主線程是由操做系統自動開啓的,而子線是由程序主動開啓
3.即時主線程的代碼執行完畢,也不會結束進程,會等待全部線程執行完畢,進程才結束
開啓線程的兩種方式:
1.實例化Tread類,target參數用於指定子線程要執行的任務
from threading import Thread def task(): print("子線程 run........") t = Thread(target=task) t.start() print("over")
2.繼承Tread類,覆蓋run方法
from threading import Thread class MyThread(Thread): def run(self): print("子線程 run........") t = MyThread() t.start() print("over")
與進程在使用方法上沒有任何區別,不一樣的是開啓子線程的代碼能夠寫在任意位置
之因此使用方法徹底相同是由於,多進程實際上是爲了彌補多線程的缺憾而誕生的。詳見GIL鎖
線程與進程區別:
1.同一進程中 線程之間數據共享
a = 100 def task(): global a print("子線程 run........") a = 1 t = Thread(target=task) t.start() print(a) # 1 print("over")
2.建立線程的開銷遠比建立進程小的多
from threading import Thread from multiprocessing import Process import time def task(): pass if __name__ == '__main__': start = time.time() for i in range(100): p = Thread(target=task) p.start() print(time.time()-start) # 修改Thread 爲Process類 查看結果
3.不管開啓了多少子線程PID是不會變的
from threading import Thread import os def task(): print(os.getpid()) for i in range(100): p = Thread(target=task) p.start()
Tread類的經常使用屬性:
# threading模塊包含的經常使用方法 import threading print(threading.current_thread().name) #獲取當前線程對象 print(threading.active_count()) # 獲取目前活躍的線程數量 print(threading.enumerate()) # 獲取全部線程對象 t = Thread(name="aaa") # t.join() # 主線程等待子線程執行完畢 print(t.name) # 線程名稱 print(t.is_alive()) # 是否存活 print(t.isDaemon()) # 是否爲守護線程
守護線程:
設置守護線程的語法與進程相同,相同的是也必須放在線程開啓前設置,不然拋出異常。
守護線程的特色:
守護線程會在被守護線程結束後當即結束
from threading import Thread import time def task(): print("start......") time.sleep(5) print("end......") t = Thread(target=task) # t.setDaemon(True) t.daemon = True t.start() print("main over!")
疑惑:
from threading import Thread import time def task(): print("start....1") time.sleep(3) print("end......1") def task2(): print("start....2") time.sleep(4) print("end......2") t = Thread(target=task) t.daemon = True t.start() t2 = Thread(target=task2) t2.start() print("main over!")
打印main over後主線程代碼執行完畢,可是守護線程t1並無當即結束,這是什麼緣由呢?
答:主線程會等待全部子線程執行完畢後結束
在上述例子中,一共有三個線程,主線程 ,t1,t2
雖然t1是守護線程 ,可是t2並非因此主線程會等待t2執行結束才結束
順序是:守護線程 等待 主線程 等待 其他子線程
換句話說,守護線程會隨着全部非守護線程結束而結束。
線程鎖
互斥鎖
多線程的最主要特徵之一是:同一進程中全部線程數據共享
一旦共享必然出現競爭問題。
a = 10 #lock = Lock() def task(): global a #lock.acquire() b = a - 1 time.sleep(0.1) a = b #lock.release() for i in range(10): t = Thread(target=task) t.start() for t in threading.enumerate(): if t != threading.current_thread(): t.join() print(a) # 輸出 9
當多個線程要併發修改同一資源時,也須要加互斥鎖來保證數據安全。
一樣的一旦加鎖,就意味着串行,效率必然下降。
死鎖
現有兩把鎖l1和l2 用於表示盤子和筷子
兩個線程的目標是吃飯,要吃飯的前提是同時拿到筷子和盤子,可是兩我的的目標不一樣一個先拿筷子 ,一個先拿盤子最終形成死鎖
l1 = Lock() l2 = Lock() def task(): l1.acquire() print(threading.current_thread().name,"拿到了筷子") time.sleep(0.1) l2.acquire() print(threading.current_thread().name, "拿到了盤子") print("吃飯") l1.release() l2.release() def task2(): l2.acquire() print(threading.current_thread().name, "拿到了盤子") l1.acquire() print(threading.current_thread().name,"拿到了筷子") print("吃飯") l2.release() l1.release() t1 = Thread(target=task) t1.start() t2 = Thread(target=task2) t2.start()
共有兩把鎖,可是一人拿到了一把,而且互不釋放,相互等待,致使程序卡死,這就死鎖。
要發生死鎖只有兩種狀況
1.有不止一把鎖,不一樣線程或進程分別拿到了不一樣的鎖不放
2.對同一把鎖執行了屢次acquire
其中第二種狀況咱們能夠經過可重入鎖來解決
可重入鎖
Rlock 同一個線程能夠屢次執行acquire,釋放鎖時,有幾回acquire就要release幾回。
可是本質上同一個線程屢次執行acquire時沒有任何意義的,其餘線程必須等到RLock所有release以後才能訪問共享資源。
因此Rlock僅僅是幫你解決了代碼邏輯上的錯誤致使的死鎖,並不能解決多個鎖形成的死鎖問題
# 同一把RLock 屢次acquire #l1 = RLock() #l2 = l1 # 不一樣的RLock 依然會鎖死 #l1 = RLock() #l2 = RLock() def task(): l1.acquire() print(threading.current_thread().name,"拿到了筷子") time.sleep(0.1) l2.acquire() print(threading.current_thread().name, "拿到了盤子") print("吃飯") l1.release() l2.release() def task2(): l2.acquire() print(threading.current_thread().name, "拿到了盤子") l1.acquire() print(threading.current_thread().name,"拿到了筷子") print("吃飯") l2.release() l1.release() t1 = Thread(target=task) t1.start() t2 = Thread(target=task2) t2.start()
忠告:在處理併發安全時 用完公共資源後必定要釋放鎖
信號量
Semaphore
信號量也是一種鎖,其特殊之處在於可讓一個資源同時被多個線程共享,並控制最大的併發訪問線程數量。
若是把Lock比喻爲家用洗手間,同一時間只能一我的使用。
那信號量就能夠看作公共衛生間,同一時間能夠有多我的同時使用。
from threading import Thread,Semaphore,current_thread import time s = Semaphore(3) def task(): s.acquire() print("%s running........" % current_thread()) time.sleep(1) s.release() for i in range(20): Thread(target=task).start()