生成器原理
使用生成器實現多工
gevent實現非同步
協程核心思想
asyncio實現非同步
從python2到python3,協程經歷了翻天覆地的變化。
協程的底層架構是在 pep 342 中定義,在 python2.5 實現的。
實現思想:使用yield掛起生成器,使用send 方法啟用生成器。這樣生成器就具備實現協程的功能。
send方法類似於next,不同的是send方法可以修改值,next不能修改只能取下乙個。
執行 generator.send(none) 完全等同於呼叫生成器的 next 方法。使用其它引數呼叫 send 也有同樣的效果,不同的是,當前生成器表示式產生的值會不一樣。
python2 中生成器中 return 語句提供值,會丟擲 syntaxerror 異常。
python3 中生成器可以 return 乙個值,
python3.3 增加了 yield from 語法,使呼叫巢狀生成器變得簡單
python3.5 加入了關鍵字 async 和 await ,將生成器和協程徹底分開。
python 直譯器是基於棧的,其中有三種棧:呼叫棧 (frame stack)、資料棧 (data stack)、塊棧 (block statck)。
對於python編譯器來說,pycodeobject物件是其真正的編譯結果。
在**執行時,位元組碼會儲存在 pycodeobject 物件中。pycodeobject 儲存的是編譯後的靜態資訊,在執行的時候會結合上下文形成乙個完整的執行態環境。函式的__code__
變數其實就是函式的編譯後的pycodeobject物件
yield_value 指令,掛起當前函式
呼叫生成器函式時,生成器不會立即執行,而是返回乙個生成器物件。生成器物件有 gi_code 和 gi_frame 兩個常用的屬性,gi_code 和 code 一樣存放了位元組碼,gi_frame 中儲存了執行時的資料。
使用 send 方法也可以使生成器繼續執行,和 next 不同的是,send 方法可以向生
成器傳入值
yield 之後和之前的**執行是分離的,函式在遇到 yield 會交出控制權,send
方法會重新獲取控制權。
原理:函式遇到yield掛起,就會去執行別的任務。
缺點:雖然實現了併發,但是不能檢測io阻塞(例如sleep)。
import time
defcalc_time
(func)
:def
(*args,
**kwargs)
: start_time = time.time(
) result = func(
*args,
**kwargs)
end_time = time.time(
)print
(f'執行時間是:'
)return result
defwork1()
:for i in
range(5
):print
(f'聽**....'
) time.sleep(1)
yield
defwork2()
:for i in
range(5
):time.sleep(1)
print
(f'打遊戲....'
)yield
@calc_time
defmain()
: g1 = work1(
) g2 = work2(
)while
true
:try
:next
(g1)
next
(g2)
except stopiteration:
# 沒有下乙個next取不到資料的時候報這個異常
break
main(
)
gevent 是應用非常廣泛的非同步網路庫,底層使用 greenlet 協程
gevent能自動檢測io阻塞,遇到阻塞自動切換任務。
import time
import gevent
from gevent import monkey # gevent的補丁,為了解決gevent遇到time.sleep不切換的問題
monkey.patch_all(
)# 使用gevent的補丁
defcalc_time
(func)
:def
(*args,
**kwargs)
: start_time = time.time(
) result = func(
*args,
**kwargs)
end_time = time.time(
)print
(f'執行時間是:'
)return result
defwork1()
:for i in
range(5
):print
(f'聽**....'
)# time.sleep(1)
gevent.sleep(60*
5)defwork2()
:for i in
range(5
):time.sleep(1)
# gevent能自動檢測io阻塞,遇到阻塞自動切換任務
print
(f'打遊戲....'
)@calc_time
defmain()
: g1 = gevent.spawn(work1)
# 建立協程1
g2 = gevent.spawn(work2)
# 建立協程2
g1.join(
) g2.join(
)if __name__ ==
'__main__'
: main(
)
協程非常安全,遇到阻塞自動切換。
為了實現非同步io。
協程是為了保證執行緒安全設計的。
若干個協程任務,當某個任務遇到阻塞時,自動切換到非阻塞的任務上。
阻塞: io阻塞。 如:
import time
import asyncio
defcalc_time
(func)
:def
(*args,
**kwargs)
: start_time = time.time(
) result = func(
*args,
**kwargs)
end_time = time.time(
)print
(f'執行時間是:'
)return result
async
defwork1()
:# 每乙個方法錢都要加async標識
for i in
range(5
):await asyncio.sleep(1)
# 需要耗時的任務前加await,自動切換到其他任務
print
(f'聽**....'
)async
defwork2()
:for i in
range(5
):await asyncio.sleep(1)
print
(f'打遊戲....'
)async
defmain()
: task1 = asyncio.create_task(work1())
# 建立協程任務
task2 = asyncio.create_task(work2())
await task1 # 啟動協程
await task2
if __name__ ==
'__main__'
: asyncio.run(main(
))
使用者態 : 切換不需要cpu排程
核心態: 執行緒切換,程序切換,核心態 , 切換需要cpu排程
python協程與非同步協程
在前面幾個部落格中我們一一對應解決了消費者消費的速度跟不上生產者,浪費我們大量的時間去等待的問題,在這裡,針對業務邏輯比較耗時間的問題,我們還有除了多程序之外更優的解決方式,那就是協程和非同步協程。在引入這個概念之前我們先看 看這個圖 從這個我們可以看出來,假如來了9個任務,即使我們開了多程序,在業...
python協程使用 協程的案例
概念 使用者層面在乙個執行緒中進行多工切換的機制,比執行緒更加輕量級 實現併發量更大 協程的使用 使用第三方庫 gevent gevent 是乙個基於協程的 python 網路庫,在遇到 io 阻塞時,程式會自動進行切換,可以讓我們用同步的放肆寫非同步 io 協程的使用 from gevent im...
python 併發程式設計 協程 協程介紹
協程 是單執行緒下的併發,又稱微執行緒,纖程。英文名coroutine。一句話說明什麼是執行緒 協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的 需要強調的是 1.python的執行緒屬於核心級別的,即由作業系統控制排程 如單執行緒遇到io或執行時間過長就會被迫交出cpu執行許可...