1、協程概念
協程:稱爲微線程,是一種用戶態的輕量級線程。
發展歷程:
(1)最初的生成器變形yied/send;
(2)引入@asyncio.coroutine 和 yield from
(3)在python3.5版本中引入了async和await關鍵字
【協程理解】
(1)普通理解:線程是級別的,他們是又操作系統調度;協程是程序級別的,由程序員根據需要自己調度。我們把一個線程中的一個個函數稱爲子程序。那麼子程序在執行過程中可以中斷執行別的子程序。別的子程序也可以中斷回來繼續執行之前的子程序,這就是協程。
子程序:在所有的語言中都是層級調用,是通過棧實現的,一個線程就是執行一個子程序,子程序的調用總是一個入口,一次返回,調用的順序是明確的。
(2)專業理解:協程擁有自己的寄存器上下文和棧,協程在調度切換時,將寄存器上下文和棧保存到其他的地方,在切回時,恢復先前保存的寄存器上下文和棧。因此,協程能保留上一次調用時的狀態。
因此每次過程重入時,就相當於進入上一次調用的狀態。
【協程優點】
(1)無需線程上下文切換的開銷,協程避免了無意義的調度,由此提高了性能,但是,程序必須自己承擔調度的責任,同時協程也失去了標準線程使用多cpu的能力。
(2)無需原子操作鎖定及同步的開銷。
(3)方便切換控制流,簡化編程模型。
(4)高併發+高擴展性+低成本,一個cpu支持上萬個協程不是問題。
【協程缺點】
(1)無法利用多核資源,協程的本質是單個線程,它不能同時將單個cpu的多個核使用,協程需要和進程配合使用才能運行在多核cpu上。但是一般不需要,除非是cpu密集型的應用。
(2)進行阻塞操作(耗時IO)會阻塞程序
【迭代傳遞數據】
####數據傳遞
def fun():
data = "#"
# yield 不但可以返回一個值,並且它還可以接收調用者發送的參數
r =yield data
print("-------------1-------------------",r,data)
r =yield data
print("-------------2-------------------",r,data)
r =yield data
print("-------------3-------------------",r,data)
r = yield data
g = fun()
# print(g,type(g)) # <generator object fun at 0x000001C73A4534F8> <class 'generator'>
# next無法傳參,只能拿到返回值,傳遞參數需要使用send
print(g.send(None)) # send就是啓動
print(g.send("a"))
print(g.send("b"))
print(g.send("c"))
# print(next(g))
【異步】
異步是和同步相對的,指在處理調用這個事務之後,不會等待這個事務的處理結果,直接去處理第二個事務了,通過狀態、通知、回調來通知調用者處理結果。
import time
import asyncio
# async就是定義異步函數(也就是定義一個協程)
async def func():
# 模擬一個耗時IO操作
asyncio.sleep(1)
print("time:%s" % time.time())
loop = asyncio.get_event_loop()
for i in range(5):
loop.run_until_complete(func())
2、asyncio模塊
python3.5內置了asyncio對異步IO支持。 編程模式:是有一個消息循環,我們從asyncio模塊中直接獲取一個EventLoop的引用,然後把需要執行的協程仍到EventLoop中執行,就實現了異步。 說明:到目前爲止實現協程的不僅只有asyncio,還有gevent和tornado都實現了類似的功能。 【關鍵字】 (1)event_loop 事件循環:程序開啓一個無限循環也就是死循環,把一些函數註冊到事件循環中;當滿足條件發送時,調用相應的協程函數 (2)coroutine 協程:協程對象,指一個使用async關鍵字定義的函數,它的調用不會立即執行函數,而是會返回一個協程對象。協程對象需要註冊到事件循環中,是由事件循環調用。 (3) task 任務:一個協程對象就是一個原生可以掛起的函數,任務則是對協程進一步的封裝,其中包含了任務的各種狀態。 (4) future:英文單詞含義是將來,代表將來執行或沒有執行的任務的結果,它和task和是哪個沒有本質區別; (5)async/await:python3.5用定義協程的關鍵字。async定義協程,await用於掛起阻塞異步調用接口。
2.1、定義一個協程
import time
import asyncio
# 通過async關鍵字定義了一個協程,,協程不能直接運行,需要將協程加入到事件循環中
async def run(x):
print("waiting:%d" %x)
start = time.time()
coroutine = run(2) # 得到一個協程對象,這個時候run()函數沒有執行
print(coroutine) # <coroutine object run at 0x000002B07ADC74C8> 發現沒有調用
loop = asyncio.get_event_loop() # 創建一個事件循環(注意:其實真是情況是asyncio模塊中獲取一個引用)
# 將協程對象加入到事件循環中
loop.run_until_complete(coroutine)
end = time.time()
print("Time:",end-start)
運行結果如下:
<coroutine object run at 0x000002351F018448>
waiting:2
Time: 0.006769657135009766
2.2、定義一個task任務
import asyncio
import time
async def run(x):# 定義協程對象
print("waiting:%d" %x)
start = time.time()
coroutine = run(2) # 得到一個協程對象,這個時候run()函數沒有執行
print(coroutine)
loop = asyncio.get_event_loop() # 創建一個事件循環(注意:其實真是情況是asyncio模塊中獲取一個引用)
# 將協程對象加入到事件循環中,協程對象不能直接運行,在註冊事件循環的時候,其實是run_until_complete()方法將協程對象包裝成了一個任務對象
# task任務對象是Future類的子類對象,保存了協程運行後的狀態,用於未來獲取協程的結果。
# loop.run_until_complete(coroutine)
# 創建任務
task = asyncio.ensure_future(coroutine)
# 第二種創建任務方式 task = loop.create_task(coroutine)
print(task)
#將任務加入事件循環中
loop.run_until_complete(task)
end = time.time()
print("Time:",end-start)
運行結果如下:
<coroutine object run at 0x0000021B568B9448>
<Task pending coro=<run() running at D:/py_workspace/spider_project/01異步/2創建一個任務task.py:6>>
waiting:2
Time: 0.0035004615783691406
2.3、協程綁定回調
import asyncio
import time
async def run(x):
print("waiting:%d" %x)
return "done after %d" %x
def callback(future): # 定義一個回調函數,參數爲future,任務對象
print("callback",future.result())
start = time.time()
coroutine = run(2) # 得到一個協程對象,這個時候run()函數沒有執行
loop = asyncio.get_event_loop() # 創建一個事件循環(注意:其實真是情況是asyncio模塊中獲取一個引用)
task = asyncio.ensure_future(coroutine) # 創建任務
task.add_done_callback(callback) # 給任務添加回調,在任務結束後調用回調函數
loop.run_until_complete(task)#將任務加入事件循環中
end = time.time()
print("Time:",end-start)
運行結果:
waiting:2
callback done after 2
Time: 0.0018661022186279297
2.4、阻塞和await
import asyncio
import time
async def run(x):
# async定義協程對象,使用await可以針對耗時的操作進行掛起,就像生成器yield一樣,函數交出控制權。
# 協程遇到await事件循環將會掛起該協程,執其他協程,直到其他協程也掛起或者執行完畢,再進行下一個協程的執行
print("waiting:%d" % x)
# 調用一個耗時IO操作
await asyncio.sleep(x) # asyncio.sleep另一個協程對象
return "done after %d over" % x
def callback(future):
print("callback:",future.result())
start = time.time()
coroutine = run(10) # 得到一個協程對象,這個時候run()函數沒有執行
loop = asyncio.get_event_loop() # 創建一個事件循環(注意:其實真是情況是asyncio模塊中獲取一個引用)
task = asyncio.ensure_future(coroutine) # 創建任務
task.add_done_callback(callback)
loop.run_until_complete(task)#將任務加入事件循環中
end = time.time()
print("Time:",end-start)
運行結果:
waiting:10
callback: done after 10 over
Time: 10.019726753234863