python GIL鎖 鎖 線程池 生產者消費模型
python的GIL 鎖
python內置的一個全局解釋器鎖 , 鎖的作用就是保證同一時刻一個進程中只有一個線程可以被cpu調度
為什麽有這把GIL鎖?
python語言的創始人在開發這門語言時 , 目的快速把語言開發出來 , 如果加上GIL鎖(C語言加鎖) , 切換時按照100條字節指令來進行線程間的切換
鎖 :
1.鎖 : Lock(1次放1個)
線程安全 , 多線程操作時 , 內部會讓所有線程排隊處理 , 如 : list / dict / Queue
線程不安全 + 人 =>排隊處理
需求:
a:創建100個進程 , 在列表中追加8
b:創建100個線程
v = []
鎖
把自己添加到列表中
在讀取列表的最後一個
解鎖
import threading import time v = [] lock = threading.Lock() def func(arg): lock.acquire() v.append(arg) time.sleep(0.01) m = v[-1] print(arg,m) lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
2.鎖 : RLock(1次放1個)
import threading import time v = [] lock = threading.RLock() def func(arg): lock.acquire() lock.acquire() v.append(arg) time.sleep(0.01) m = v[-1] print(arg,m) lock.release() lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
3鎖 : BoundedSemaphore (1次放N個) 信號量
import time import threading lock = threading.BoundedSemaphore(3) def func(arg): lock.acquire() print(arg) time.sleep(1) lock.release() for i in range(20): t =threading.Thread(target=func,args=(i,)) t.start()
4.鎖 : Condition (1次方法x個)
import time import threading lock = threading.Condition() def func(arg): print(‘線程進來了‘) lock.acquire() lock.wait() # 加鎖 print(arg) time.sleep(1) lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() while True: inp = int(input(‘>>>‘)) lock.acquire() lock.notify(inp) lock.release()
5.鎖 : Event(1次放所有)
import time import threading lock = threading.Event() def func(arg): print(‘線程來了‘) lock.wait() # 加鎖:紅燈 print(arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() input(">>>>") lock.set() # 綠燈 lock.clear() # 再次變紅燈 for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() input(">>>>") lock.set()
以上例子 : 線程安全 , 列表和字典線程安全
為什麽要加鎖?
非線程安全
控制一段代碼
6. threading.local
作用 : 內部自動會為每個線程維護一個空間(字典) 用於當前存取屬於自己的值 , 保證線程之間的數據隔離
{
線程ID: {...}
線程ID: {...}
線程ID: {...}
線程ID: {...}
}
import time import threading v = threading.local() def func(arg): # 內部會為當前線程創建一個空間用於存儲:phone=自己的值 v.phone = arg time.sleep(2) print(v.phone,arg) # 去當前線程自己空間取值 for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
原理方法如下
import time import threading DATA_DICT = {} def func(arg): ident = threading.get_ident() DATA_DICT[ident] = arg time.sleep(1) print(DATA_DICT[ident],arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
7. 線程池
from concurrent.futures import ThreadPoolExecutor import time def task(a1,a2): time.sleep(2) print(a1,a2) # 創建了一個線程池(最多5個線程) pool = ThreadPoolExecutor(5) for i in range(40): # 去線程池中申請一個線程,讓線程執行task函數。 pool.submit(task,i,8)
8.生產者消費者模型
三部件 :
生產者
隊列 , 先進先出
擴展 : 棧 , 後進先出
消費者
問 : 生產者消費者模型解決了啥問題? 不用一直等待問題
import time import queue import threading q = queue.Queue() # 線程安全 def producer(id): """ 生產者 :return: """ while True: time.sleep(2) q.put(‘包子‘) print(‘廚師%s 生產了一個包子‘ %id ) for i in range(1,4): t = threading.Thread(target=producer,args=(i,)) t.start() def consumer(id): """ 消費者 :return: """ while True: time.sleep(1) v1 = q.get() print(‘顧客 %s 吃了一個包子‘ % id) for i in range(1,3): t = threading.Thread(target=consumer,args=(i,)) t.start()
鎖 線程 線程池 threading.local 生成著消費者模型
概念性理解 配合代碼
python GIL鎖 鎖 線程池 生產者消費模型