死鎖指的是某個資源被佔用後,一直得不到釋放,致使其餘須要這個資源的線程進入阻塞狀態.python
與普通的區別程序員
解決方法安全
同一個線程必須保證,加鎖的次數和解鎖的次數相同,其餘線程纔可以搶到這把鎖服務器
能夠限制同時並執行公共代碼的線程數量網絡
若是限制數量爲1,則與普通互斥鎖沒有區別(默認爲1)多線程
from threading import Semaphore,current_thread,Thread import time s = Semaphore(2) def task(): s.acquire() time.sleep(1) print(current_thread().name) s.release() for i in range(10): Thread(target=task).start() # 結果是每次都會執行兩個子線程
什麼是GIL鎖?併發
在cpython中,全局解釋器鎖(GIL,是爲了阻止多個本地線程在同一時間執行python字節碼的互斥鎖,app
由於cpython的內存管理是非線程安全的,這個鎖是很是必要的,由於其餘愈來愈多的特性依賴這個特性.異步
''' In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) '''
線程安全問題具體的表現函數
cpython解釋器與python程序之間的關係:
GC線程: 執行python變量名管理的線程
python會自動幫咱們處理垃圾,清掃垃圾也是一堆代碼,所以也須要開啓一個線程來執行,這個線程就是GC線程.
而GC線程與咱們程序中的線程就會產生安全問題
例如: 線程a 要定義一個變量
若是在進行到第二步的時候,CPU切換到了GC線程,GC線程就會把這個值當垃圾清理掉,這就會形成線程安全問題.
GIL是一把互斥鎖,互斥鎖將致使效率下降
具體表現:
在cpython即使開啓了多線程,並且CPU也是多核的,卻沒法並行執行任務,由於解釋器只有一個,同一時間只有一個任務在執行.
沒辦法解決,只能儘量的避免GIL鎖影響咱們的效率
使用多進程可以實現並行,從而更好的利用多核CPU
對任務進行區分(分爲兩類): (重點)
計算密集型:
基本沒有IO 大部分時間在計算,例如:人臉識別/圖像處理
因爲多線程不能 並行,因此應該使用多進程,將任務分給不一樣CPU核心
IO密集型:
因爲網絡IO速度對比CPU處理速度很是慢多線程並不會形成太大的影響
另外若有大量客戶端鏈接服務,進程根本開不起來,只能用多線程
之因此加鎖是爲了解決線程安全問題,可是有了鎖,致使cpython中多線程不能並行,只能併發
可是並不能急就此否定python,有一下幾點緣由
性能測試
from multiprocessing import Process from threading import Thread import time # # 計算密集型任務 # # def task(): # for i in range(100000000): # 1+1 # # # if __name__ == '__main__': # start_time = time.time() # # ps = [] # for i in range(5): # p = Process(target=task) # # p = Thread(target=task) # p.start() # ps.append(p) # # for i in ps:i.join() # # print("共耗時:",time.time()-start_time) # 多進程勝 # IO密集型任務 def task(): for i in range(100): with open(r"1.死鎖現象.py",encoding="utf-8") as f: f.read() if __name__ == '__main__': start_time = time.time() ps = [] for i in range(10): p = Process(target=task) # p = Thread(target=task) p.start() ps.append(p) for i in ps:i.join() print("共耗時:",time.time()-start_time) # 多線程勝
爲何要裝到容器中
若是進程不結束,池子裏面的進程或者線程也是一直存活的
import os import time from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor from threading import activeCount,enumerate,currentThread # # 建立一個線程池 指定最多能夠容納兩個線程 # pool = ThreadPoolExecutor(20) # # def task(): # print(currentThread().name) # # # 提交任務到池子中 # pool.submit(task) # pool.submit(task) # # print(enumerate()) # 進程池的使用 def task(): time.sleep(1) print(os.getpid()) if __name__ == '__main__': pool = ProcessPoolExecutor(2) pool.submit(task) pool.submit(task) pool.submit(task)
同步:
指的是,提交任務後必須在原地等待,直到任務結束. 同步不等於阻塞
異步:
指的是,提交任務後不須要在原地等待,能夠繼續往下執行代碼
異步效率高於同步,異步任務將致使一個問題:就是任務的發起方不知道任務什麼時候處理完畢
異步/同步指的是提交任務的方式
解決方法:
輪詢: 每隔一段時間就問一次
效率低,沒法及時獲取結果 (不推薦使用)
異步回調: 讓任務的執行方主動通知
能夠及時拿到任務的結果,(推薦方式)
# 異步回調 from threading import Thread # 具體的任務 def task(callback): print("run") for i in range(100000000): 1+1 callback("ok") #回調函數 參數爲任務的結果 def finished(res): print("任務完成!",res) print("start") t = Thread(target=task,args=(finished,)) t.start() #執行task時 沒有致使主線程卡主 而是繼續運行 print("over")
線程池中回調的使用
# 使用案例: def task(num): time.sleep(1) print(num) return "hello python" def callback(obj): print(obj.result()) pool = ThreadPoolExecutor() res = pool.submit(task,123) res.add_done_callback(callback) print("over")