從頭造輪子:python3 asyncio之 run(2)


前言

書接上文,本文造第二個輪子,也是asyncio包里面非常常用的一個函數run

一、知識准備

● 相對於run_until_complete,改動並不大,就是將入口函數重新封裝了一下,基礎知識主要還是run_until_complete的內容
● asyncio.run是Python3.7之后新增的入口函數


二、環境准備

組件 版本
python 3.7.7

三、run的實現

先來看下官方asyncio的使用方法:

|># more main.py
import asyncio
async def hello():
    print('enter hello ...')
    return 'world'

if __name__ == "__main__":
    rst = asyncio.run(hello())
    print(rst)
    
|># python3 main.py
enter hello ...
return world ...

來看下造的輪子的使用方式:

▶ more main.py
from wilsonasyncio import run

async def hello():
    print('enter hello ...')
    return 'return world ...'

if __name__ == "__main__":
    ret = run(hello())
    print(ret)

    
▶ python3 main.py
enter hello ...
return world ...

自己造的輪子也很好的運行了,下面我們來看下輪子的代碼

四、代碼解析

輪子代碼

1)代碼組成

|># tree
.
├── eventloops.py 
├── futures.py
├── main.py
├── tasks.py
├── wilsonasyncio.py
文件 作用
eventloops.py 事件循環
futures.py futures對象
tasks.py tasks對象
wilsonasyncio.py 可調用方法集合
main.py 入口

2)代碼概覽:

eventloops.py

類/函數 方法 對象 作用 描述
Eventloop 事件循環,一個線程只有運行一個
__init__ 初始化兩個重要對象 self._readyself._stopping
self._ready 所有的待執行任務都是從這個隊列取出來,非常重要
self._stopping 事件循環完成的標志
call_soon 調用該方法會立即將任務添加到待執行隊列
run_once run_forever調用,從self._ready隊列里面取出任務執行
run_forever 死循環,若self._stopping則退出循環
run_until_complete 非常重要的函數,任務的起點和終點(后面詳細介紹)
create_task 將傳入的函數封裝成task對象,這個操作會將task.__step添加到__ready隊列
Handle 所有的任務進入待執行隊列(Eventloop.call_soon)之前都會封裝成Handle對象
__init__ 初始化兩個重要對象 self._callbackself._args
self._callback 待執行函數主體
self._args 待執行函數參數
_run 待執行函數執行
get_event_loop 獲取當前線程的事件循環
_complete_eventloop 將事件循環的_stopping標志置位True
run 入口函數 新增

tasks.py

類/函數 方法 對象 作用 描述
Task 繼承自Future,主要用於整個協程運行的周期
__init__ 初始化對象 self._coro ,並且call_soonself.__step加入self._ready隊列
self._coro 用戶定義的函數主體
__step Task類的核心函數
ensure_future 如果對象是一個Future對象,就返回,否則就會調用create_task返回,並且加入到_ready隊列 新增

futures.py

類/函數 方法 對象 作用 描述
Future 主要負責與用戶函數進行交互
__init__ 初始化兩個重要對象 self._loopself._callbacks
self._loop 事件循環
self._callbacks 回調隊列,任務暫存隊列,等待時機成熟(狀態不是PENDING),就會進入_ready隊列
add_done_callback 添加任務回調函數,狀態_PENDING,就虎進入_callbacks隊列,否則進入_ready隊列
set_result 獲取任務執行結果並存儲至_result,將狀態置位_FINISH,調用__schedule_callbacks
__schedule_callbacks 將回調函數放入_ready,等待執行
result 獲取返回值

3)執行過程

3.1)入口函數

main.py

async def hello():
    print('enter hello ...')
    return 'return world ...'

if __name__ == "__main__":
    ret = run(hello())
    print(ret)
  • ret = run(hello())直接調用run,參數是用戶函數hello(),我們看下run的源碼
def run(main):
    loop = get_event_loop()
    return loop.run_until_complete(main)
  • loop = get_event_loop()獲取事件循環
  • return loop.run_until_complete(main)調用run_until_complete

3.2)事件循環啟動

    def run_until_complete(self, future):
        future = tasks.ensure_future(future, loop=self)
        future.add_done_callback(_complete_eventloop, future)
        self.run_forever()
        return future.result()
  • 與之前略有不同,future = tasks.ensure_future(future, loop=self),調用了tasks.ensure_future
def ensure_future(coro_or_future, *, loop=None):
    if isinstance(coro_or_future, Future):
        return coro_or_future
    else:
        return loop.create_task(coro_or_future)
  • 如果傳入的對象是一個普通函數,那就封裝成一個task;如果已經是一個future對象,那就直接返回。這一步的目的主要是確保傳入的對象,是一個Future類型

剩下的部分已經沒有什么新鮮的了,和run_until_complete一樣,我們直接跳過...

3.7)執行結果

▶ python3 main.py
enter hello ...
return world ...

五、流程總結

六、小結

runrun_until_complete大同小異,只不過入口函數做了一些調整,使得用戶調用更加的便利
● 本文中的代碼,參考了python 3.7.7中asyncio的源代碼,裁剪而來
● 本文中代碼:代碼



至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...
更多文章,請關注我:wilson.chai


免責聲明!

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



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