From ecd049c8429c335a56e584653b6097e951727178 Mon Sep 17 00:00:00 2001 From: asahi Date: Tue, 19 Aug 2025 12:55:16 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E9=98=85=E8=AF=BBpython=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/py.md | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/python/py.md b/python/py.md index bf283b8..bac26fc 100644 --- a/python/py.md +++ b/python/py.md @@ -120,6 +120,15 @@ - [iterators](#iterators) - [generators](#generators) - [generator expression](#generator-expression) + - [asyncio](#asyncio) + - [Coroutines](#coroutines) + - [Awaitables](#awaitables) + - [Coroutinues](#coroutinues) + - [Tasks](#tasks) + - [Futures](#futures) + - [Creating Tasks](#creating-tasks) + - [`asyncio.create_task(coro, *, name=None, context=None)`](#asynciocreate_taskcoro--namenone-contextnone) + - [Task Cancellation](#task-cancellation) # Python @@ -2035,3 +2044,155 @@ generator令iterator的编写更加简单。 >>> list(data[i] for i in range(len(data)-1, -1, -1)) ['f', 'l', 'o', 'g'] ``` + +## asyncio +python支持通过`async/await`语法编写并发代码。asyncio是多个python异步框架的基础。 + +### Coroutines +可以通过`async/await`的语法来编写asyncio application。例如,如下代码将打印`hello`并等待1s,然后打印`world`: +```py +>>> import asyncio + +>>> async def main(): + print('hello') + await asyncio.sleep(1) + print('world') + +>>> asyncio.run(main()) +hello +world +``` +如果仅简单调用`main()`,并不会对该coroutinue进行调度: +```py +>>> main() + +``` + +为了实际的运行coroutinue,asyncio提供了如下机制: +- `asyncio.run`:在上述示例中,`asyncio.run`用于执行main函数的entry point +- `awaiting on coroutinue`:如下代码片段将会在1s后打印`hello`,并在2s后打印`world` + ```py + import asyncio + import time + + async def say_after(delay, what): + await asyncio.sleep(delay) + print(what) + + async def main(): + print(f"started at {time.strftime('%X')}") + + await say_after(1, 'hello') + await say_after(2, 'world') + + print(f"finished at {time.strftime('%X')}") + + asyncio.run(main()) + ``` + 上述示例总共耗费3s,首先等待`hello` 1s,再等待`world` 2s +- `asyncio.create_task()`:该方法支持创建Tasks让多个coroutinue并发运行 + ```py + async def main(): + task1 = asyncio.create_task( + say_after(1, 'hello')) + + task2 = asyncio.create_task( + say_after(2, 'world')) + + print(f"started at {time.strftime('%X')}") + + # Wait until both tasks are completed (should take + # around 2 seconds.) + await task1 + await task2 + + print(f"finished at {time.strftime('%X')}") + ``` + 上述示例总共耗费2s,等待`hello` 1s,`hello`打印后再过1s打印`world` + +- `asyncio.TaskGroup`:该方法相较`asyncio.create_task`提供了一个更加现代的api,示例如下: + ```py + async def main(): + async with asyncio.TaskGroup() as tg: + task1 = tg.create_task( + say_after(1, 'hello')) + + task2 = tg.create_task( + say_after(2, 'world')) + + print(f"started at {time.strftime('%X')}") + + # The await is implicit when the context manager exits. + + print(f"finished at {time.strftime('%X')}") + ``` + 当前示例总体耗时也为2s + +### Awaitables +当对象可以在await表达式中使用时,该对象被称为Awaitable object。许多asyncio api被定义为接收awaitable object。 + +总共有3中awaitables:`coroutinue, Tasks, Futures` + +#### Coroutinues +python中coroutinue是awaitable,并且可以在其他coroutinue中通过`await`调用: +```py +import asyncio + +async def nested(): + return 42 + +async def main(): + # Nothing happens if we just call "nested()". + # A coroutine object is created but not awaited, + # so it *won't run at all*. + nested() # will raise a "RuntimeWarning". + + # Let's do it differently now and await it: + print(await nested()) # will print "42". + +asyncio.run(main()) +``` +#### Tasks +`Tasks`用于并发的对coroutinues进行调度。 + +当通过`asyncio.create_task()`将coroutinue封装在Task中时,coroutinue将会自动的被调度: +```py +import asyncio + +async def nested(): + return 42 + +async def main(): + # Schedule nested() to run soon concurrently + # with "main()". + task = asyncio.create_task(nested()) + + # "task" can now be used to cancel "nested()", or + # can simply be awaited to wait until it is complete: + await task + + asyncio.run(main()) +``` + +#### Futures +`Future`是一个底层的awaitable对象,代表一个异步操作的最终结果。 + +当一个Future对象被awaited时,代表coroutinue将会等待,直至Future执行完成。 + +### Creating Tasks +#### `asyncio.create_task(coro, *, name=None, context=None)` +将coroutinue封装在Task对象中,并且对其进行调度。该方法会返回一个Task object。 + +该task将会在`get_running_loop()`返回的loop中执行,如果当前线程中没有running loop,那么将会抛出`RuntimeError`。 + +### Task Cancellation +task可以被简单和安全的取消。当task被取消时,并不会立马中断task抛出`asyncio.CancelledError`,而是等到task下一次执行await时才会抛出异常。 + +推荐通过`try/finally`来执行clean-up逻辑,当显式捕获的`asyncio.CancelledError`时,通常在clean-up操作完成后都应该对异常重新抛出。 + +asyncio中存在部分组件允许结构化的并发,例如`asyncio.TaskGroup`,其内部实现中使用了cancellation,`如果coroutinue吞了异常,将会导致asyncio中TaskGroup等组件行为异常`。 + +类似的,用户代码中通常也不应该调用`uncancel`。 + +但是,如果真的需要对`asyncio.CancelledError`进行suppress,其在吞异常之后还需要调用`uncancel`来取消cancellation state。 +