gil直譯器鎖
在cpython直譯器中,同乙個程序下開啟的多執行緒,同一時刻只能有乙個執行緒執行,無法利用多核優勢
首先需要明確的一點是gil
並不是python的特性,它是在實現python解析器(cpython)時所引入的乙個概念,所以這裡要先明確一點:gil並不是python的特性,python完全可以不依賴於gil
gil本質就是一把互斥鎖,既然是互斥鎖,所有互斥鎖的本質都一樣,都是將併發執行變成序列,以此來控制同一時間內共享資料只能被乙個任務所修改,進而保證資料安全。
可以肯定的一點是:保護不同的資料的安全,就應該加不同的鎖。
每乙個cpython程序內都有乙個gil
gil導致同一程序內的多個執行緒同一時間只能有乙個執行
之所以有gil,是因為cpython的記憶體管理不是執行緒安全的
對於計算密集型用多程序,對於io密集型用多執行緒
死鎖和遞迴鎖
所謂死鎖: 是指兩個或兩個以上的程序或執行緒在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的程序稱為死鎖程序
解決方法,遞迴鎖,在python中為了支援在同一執行緒中多次請求同一資源,python提供了可重入鎖rlock。
這個rlock內部維護著乙個lock和乙個counter變數,counter記錄了acquire的次數,從而使得資源可以被多次require。直到乙個執行緒所有的acquire都被release,其他的執行緒才能獲得資源。上面的例子如果使用rlock代替lock,則不會發生死鎖
from threading import thread,lock,rlock協程import time
# mutexa = lock()
# mutexb = lock()
mutexa = mutexb = rlock() # 乙個執行緒拿到鎖,counter加1,該執行緒內又碰到加鎖的情況,則counter繼續加1,這期間所有其他執行緒都只能等待,等待該執行緒釋放所有鎖,即counter遞減到0為止
class mythread(thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexa.acquire()
print('%s 拿到a鎖'%self.name)
mutexb.acquire()
print('%s 拿到a鎖' % self.name)
mutexb.release()
mutexa.release()
def f2(self):
mutexb.acquire()
print('%s 拿到b鎖'%self.name)
time.sleep(0.1)
mutexa.acquire()
print('%s 拿到a鎖' % self.name)
mutexa.release()
mutexb.release()
if __name__ == '__main__':
for i in range(10):
t = mythread()
t.start()
協程:是單執行緒下的併發,又稱微執行緒,纖程。英文名coroutine。一句話說明什麼是執行緒:協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的
利用yield實現單執行緒下的併發
import timegevent模組def consumer():
'''任務1:接收資料,處理資料'''
while true:
x=yield
def producer():
'''任務2:生產資料'''
g = consumer()
next(g)
for i in range(10000000):
g.send(i)
start = time.time()
# 基於yield儲存狀態,實現兩個任務直接來回切換,即併發的效果
# ps:如果每個任務中都加上列印,那麼明顯地看到兩個任務的列印是你一次我一次,即併發執行的.
producer()
stop = time.time()
print(stop-start) # 2.0272178649902344
time.sleep(2)或其他的阻塞,gevent是不能直接識別的需要用下面一行**,打補丁,就可以識別了
from gevent import monkey;monkey.patch_all()必須放到被打補丁者的前面,如time,socket模組之前
或者我們乾脆記憶成:要用gevent,需要將from gevent import monkey;monkey.patch_all()放到檔案的開頭
# pip3 install gevent# 1.切換+儲存狀態
# 2.檢測io,實現遇到io切換
from gevent import monkey
monkey.patch_all() # 將程式中的所有io操作打標記,使gevent能識別
import gevent
import time
def eat(name):
print('%s eat 1'%name)
time.sleep(2)
print('%s eat 2' % name)
def play(name):
print('%s play 1'%name)
time.sleep(3)
print('%s play 2' % name)
g1 = gevent.spawn(eat,'alex')
g2 = gevent.spawn(play,'egon')
g1.join()
g2.join()
gevent實現協程
1 yield實現 import time def task 1 while true print 1 time.sleep 0.1 yield def task 2 while true print 2 time.sleep 0.1 yield def main t1 task 1 建立迭代器 t...
gevent 協程用法
文章介紹了一種採用迴圈的方式生產協程列表,並可以向協程函式傳遞引數。協程引用 import gevent from gevent import monkey,pool monkey.patch all 初始化協程池 poolnum 10 pool pool.pool poolnum 例項化乙個協程池...
python 協程 gevent模組
import requests 匯入 gevent import gevent 由於切換是在io操作時自動完成 所以gevent需要修改python自帶的一些標準庫 這一過程在啟動時通過monkey patch 猴子補丁 完成 from gevent import monkey monkey.pat...