簡單了解一下事件循環(Event Loop)


關於我
一個有思想的程序猿,終身學習實踐者,目前在一個創業團隊任team lead,技術棧涉及Android、Python、Java和Go,這個也是我們團隊的主要技術棧。
Github:https://github.com/hylinux1024
微信公眾號:終身開發者(angrycode)

0x00 事件循環(Event Loop)

在前文《為何你還不懂得如何使用Python協程
中提到協程是通過asyncio包中的高級API來啟動的。而asyncio模塊中的核心就是事件循環(Event Loop)。它可用於執行異步任務、事件回調、執行網絡IO操作和運行子進程。官方的文檔也是建議開發者應該盡量使用asyncio包提供的高級的API,避免直接使用Event Loop對象中的方法。

系統提供的底層能力的功能模塊例如網絡連接、文件IO等都會使用到loop

大多數情況下,這些高級API可以滿足眾多使用場景,但作為一個有追求的猿類,應該要有一點點探索的精神,看看在asyncio封裝之下的Event Loop

獲取Event Loop對象
  • asyncio.get_running_loop()
    獲取當前系統線程正在使用的loop對象
  • asyncio.get_event_loop()
    獲取當前正在使用的loop對象。如果當前系統線程還沒有loop對象,那么就會創建一個新的loop對象,並使用asyncio.set_event_loop(loop)方法設置到當前系統線程中。
  • asyncio.new_event_loop()
    創建一個新的loop對象
  • asyncio.set_event_loop(loop)
    loop設置成系統線程使用的對象

Event Loop對象的常用方法

如果使用類似asyncio.run()這些高級API,以下這些方法,基本上很少會用到,建議通讀一下,大概知道loop對象擁有哪些API,了解以下底層的實現細節。

啟動和停止
  • loop.run_until_complete(future)
    future對象執行完成才返回
  • loop.run_forever()
    一直運行,直到調用了loop.stop()方法
  • loop.stop()
    停止loop對象
  • loop.is_running()
    判斷loop是否正在運行
  • loop.is_closed()
    判斷loop是否關閉
  • loop.close()
    關閉loop對象
  • coroutine loop.shutdown_asyncgens()
try:
    loop.run_forever()
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())
    loop.close()
回調方法
  • loop.call_soon(callback, *args, context=None)
    在事件循環的下一次迭代中執行callback方法,args是方法中的參數
  • loop.call_soon_threadsafe(callback, *args, context=None)
    線程安全的call_soon()
iimport asyncio
import time

def hello_world(loop):
    print('Hello World')
    time.sleep(3)  # 模擬長時間操作
    loop.stop()

loop = asyncio.get_event_loop()

# 使用loop執行 hello_world()
loop.call_soon(hello_world, loop)

# 會一直阻塞,直到調用了stop方法
try:
    loop.run_forever()
finally:
    loop.close()

可延遲的回調方法

可設置延遲執行的方法

  • loop.call_later(delay, callback, *args, context=None)
    延遲delay秒后執行
  • loop.call_at(when, callback, *args, context=None)
    在指定時間點執行
  • loop.time()
    返回當前時間
import asyncio
import datetime


def display_date(end_time, loop):
    print(datetime.datetime.now())
    if (loop.time() + 1.0) < end_time:
        # 1秒后執行
        loop.call_later(1, display_date, end_time, loop)
    else:
        loop.stop()

loop = asyncio.get_event_loop()

# 執行5秒
end_time = loop.time() + 5.0
loop.call_soon(display_date, end_time, loop)

# 一直運行,等待stop的調用
try:
    loop.run_forever()
finally:
    loop.close()

執行結果打印5秒時間點

2019-05-09 22:34:47.885412
2019-05-09 22:34:48.887513
2019-05-09 22:34:49.889396
2019-05-09 22:34:50.894316
2019-05-09 22:34:51.898457
創建Future和Tasks
  • loop.create_future()
    創建一個綁定事件循環的future對象
  • loop.create_task(coro)
    coro放入調度,並返回task對象
  • loop.set_task_factory(factory)
    設置任務工廠
  • loop.get_task_factory()
    返回任務工廠,有可能返回None
創建網絡連
coroutine loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None)

指定hostport等參數創建網絡連接

coroutine loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None)

創建UDP連接

coroutine loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None)

創建Unix連接

coroutine loop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True)

創建TCP服務

coroutine loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True)

創建Unix服務

coroutine loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None)

封裝已建立的連接,返回元組(transport, protocol)

Event Loop 的實現

asyncio 的事件循環有兩種不同的實現:SelectorEventLoopProactorEventLoop,它們的父類是AbstractEventLoop

SelectorEventLoop

這個是asyncio默認使用的Event Loop實現,支持unixwindows平台

import asyncio
import selectors

selector = selectors.SelectSelector()
loop = asyncio.SelectorEventLoop(selector)
asyncio.set_event_loop(loop)
ProactorEventLoop

這個是Windows平台專有的實現

import asyncio
import sys

if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)

0x01 總結

事件循環是asyncio的核心,asncio模塊的很多高級接口是通過封裝Event Loop對象來實現的。它提供了執行異步任務、事件回調、執行網絡IO操作和運行子進程的能力。
本文是通過官方文檔對事件循環的概念和它的常見API做了一個大概的了解。作為《前文》的補充

0x02 引用

  1. https://docs.python.org/3/library/asyncio-eventloop.html


免責聲明!

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



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