python中的協程(1)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章