python中的asyncio模塊


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM