asyncio異步IO,能夠異步網絡操作,並發,協程
1、asyncio的關鍵字說明
- event_loop事件循環:程序開啟一個無限循環,把一些函數注冊到事件循環上,當滿足事件發生的時候,調用相應的協程函數
- coroutine協程:協程對象,指一個使用async關鍵字定義的函數,它的調用不會立即執行函數,而是會返回一個協程對象,協程對象需要注冊到事件循環,由事件循環調用。
- task任務:一個協程對象就是一個原生可以掛起的函數,任務則是對協程進一步封裝,其中包含了任務的各種狀態
- future:代表將來執行或沒有執行的任務結果。它和task上沒有本質上的區別
- async/await關鍵字:async定義一個協程,await用於掛起阻塞的異步調用接口,在python3.4是使用asyncio.coroutine/yield from
2、定義一個協程
# -*-coding:utf-8 -*-
import asyncio
async def func():
print("waiting----func------")
#這里是一個協程對象,這個時候func()函數並沒有執行
coroutine = func()
print("coroutine",coroutine)
#創建一個循環時間loop
loop = asyncio.get_event_loop()
#將協程加入到事件循環loop
loop.run_until_complete(coroutine)
loop.close()
#輸出:
coroutine <coroutine object func at 0x02D10AE0>
waiting----func------
協程並發
# -*-coding:utf-8 -*-
import asyncio
#定義一個協程比普通的函數多了async關鍵字
async def a():
print("waiting ----a-----")
#在協程中掛起(釋放控制權),await后面接的方法必須是awaitable的
await asyncio.sleep(0)
print("ending ------a---------")
async def b():
print("In b")
async def main():
#並發運行任務,另一種寫法:asyncio.wait([a(),b()],)
await asyncio.gather(a(),b())
if __name__ == "__main__":
"""python3.6
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()"""
#啟動循環事件,python3.7新寫法
asyncio.run(main())
#輸出:
#waiting ----a-----
#In b
#ending ------a---------
創建一個task
協程對象不能直接運行,在注冊事件循環的時候,其實是run_until_complete方法將協程包裝成為了一個任務(task)對象,task對象是Future類的子類保存了協程運行后的狀態,用於未來獲取協程的結果
# -*-coding:utf-8 -*-
import asyncio
import time
now = lambda :time.time()
async def do_some_work(x):
print("waiting:",x)
start = now()
coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
#創建task
task = loop.create_task(coroutine)
print("task",task)
loop.run_until_complete(task)
print("task--",task)
print("Time:",now()-start)
#輸出
task <Task pending coro=<do_some_work() running at D:/soft_install/python3/python3.7/StudyHard/OneDay/CheckCrawl/asyn.py:5>>
waiting: 2
task-- <Task finished coro=<do_some_work() done, defined at D:/soft_install/python3/python3.7/StudyHard/OneDay/CheckCrawl/asyn.py:5> result=None>
Time: 0.0
由輸出可以看出創建task后,在task加入事件循環之前為pending狀態,當完成后,狀態為finished
關於上面通過loop.create_task(coroutine)創建task,同樣的可以通過 asyncio.ensure_future(coroutine)創建task
綁定回調
綁定回調,在task執行完成的時候可以獲取執行的結果,回調的最后一個參數是future對象,通過該對象可以獲取協程的返回值
# -*-coding:utf-8 -*-
import asyncio
import time
now = lambda :time.time()
async def do_some_work(x):
print("waiting:",x)
return "Done after {}s".format(x)
def callback(future):
print("callback:",future.result())
start = now()
coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
#創建task
task = asyncio.ensure_future(coroutine)
print("task:",task)
task.add_done_callback(callback)
print("task_add_callback:",task)
loop.run_until_complete(task)
print("Time:",now()-start)
#輸出
task: <Task pending coro=<do_some_work() running at D:/soft_install/python3/python3.7/StudyHard/OneDay/CheckCrawl/asyn.py:5>>
task_add_callback: <Task pending coro=<do_some_work() running at D:/soft_install/python3/python3.7/StudyHard/OneDay/CheckCrawl/asyn.py:5> cb=[callback() at D:/soft_install/python3/python3.7/StudyHard/OneDay/CheckCrawl/asyn.py:9]>
waiting: 2
callback: Done after 2s
Time: 0.0
通過add_done_callback方法給task任務添加回調函數,當task(也可以說是coroutine)執行完成的時候,就會調用回調函數。並通過參數future獲取協程執行的結果。這里我們創建 的task和回調里的future對象實際上是同一個對象
asyncio支持tcp,不支持http
http是建立在tcp之上,可以基於用socket基於tcp做http的異步io
簡單的原理如下程序:
import socket
#創建一個客戶端
client = socket.socket()
client.connect(("www.baidu.com",80,))
content="Http1.1 /index.html?k1=k2 POST k1=k2\r\n\r\nusername=a1&password=1234"
#www.baidu.com/index.html?k1=k2
content = bytes(content,encoding="utf-8")
client.sendall(content)
使用異步io做的http如下程序:
# -*-coding:utf-8 -*-
import asyncio
import time
async def fetch_async(host,url="/"):
print(host,url)
#客戶端連接服務器,reader讀取連接獲取的數據,writer是發送寫給服務器的數據
reader,writer=await asyncio.open_connection(host,80)
request_header_content = """GET %s HTTP/1.0\r\nHost:%s\r\n\r\n""" % (url,host,)
request_header_content = bytes(request_header_content,encoding="utf-8")
#寫數據,發送
writer.write(request_header_content)
await writer.drain()
#讀取,這里也會阻塞
text = await reader.read()
print(host,url,str(text,encoding="utf-8"))
writer.close()
tasks=[
fetch_async("www.cnblogs.com",'/wupeiqi/'),
fetch_async("dig.chouti.com","/pic/show?nid=4073644713430508&lid=10273091")
]
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
關於上面的基於tcp做的異步io的http可以使用aiohttp模塊,或者使用requests模塊
# -*-coding:utf-8 -*-
import asyncio
import time
import aiohttp
async def fetch_async(url):
print(url)
reponse = await aiohttp.request("GET",url)
print(url,reponse)
reponse.close()
tasks=[
fetch_async("www.cnblogs.com"),
fetch_async("dig.chouti.com")
]
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
來自:1、https://www.jianshu.com/p/2eaf07770e79
2、https://www.cnblogs.com/zhaof/p/8490045.html