当前位置:首页>python>Python 并发与 asyncio 2.2 使用 `sleep` 引入长时间运行的协程

Python 并发与 asyncio 2.2 使用 `sleep` 引入长时间运行的协程

  • 2026-10-21 13:27:12
Python 并发与 asyncio 2.2 使用 `sleep` 引入长时间运行的协程

2.2 使用 sleep 引入长时间运行的协程

我们前面的例子没有使用任何慢速操作,主要是为了帮助我们学习协程的基本语法。要真正体会到 asyncio 的好处,并展示如何同时运行多个事件,我们需要引入一些长时间运行的操作。与其马上去做网络 API 或数据库查询(这些操作所需的时间是不确定的),我们不如先通过指定等待时间来模拟长时间运行的操作。我们将使用 asyncio.sleep 函数来实现这一点。

我们可以用 asyncio.sleep 让一个协程“睡”指定的秒数。这会暂停我们的协程一段时间,模拟出像数据库或网络 API 这类耗时调用的情况。

asyncio.sleep 本身就是一个协程,所以我们必须用 await 关键字来使用它。如果我们只是直接调用它,会得到一个协程对象。由于 asyncio.sleep 是一个协程,这意味着当一个协程等待它时,其他代码就能运行。

让我们来看一个简单的例子,如下所示,它会睡 1 秒,然后打印一条 'Hello World!' 消息。

import asyncioasync def hello_world_message() -> str:    await asyncio.sleep(1)                  # ❶    return 'Hello World!'async def main() -> None:    hello_world_message()   # ❷    print(message)asyncio.run(main())
❶ 暂停  1 秒。❷ 暂停  直到  完成。

当我们运行这个应用程序时,程序会等待 1 秒,然后才打印出我们的 'Hello World!' 消息。因为 hello_world_message 是一个协程,我们用 asyncio.sleep 暂停它 1 秒,所以我们现在有 1 秒的时间可以并发运行其他代码。

接下来的几个例子中我们会频繁使用 sleep,所以让我们花点时间创建一个可重用的协程,它能帮我们睡觉并打印一些有用的信息。我们把这个协程叫做 delay。如下所示:

import asyncioasync def delay(delay_seconds: int) -> int:    print(f'sleeping for {delay_seconds} second(s)')    await asyncio.sleep(delay_seconds)    print(f'finished sleeping for {delay_seconds} second(s)')    return delay_seconds

delay 接收一个以秒为单位的持续时间参数,当函数睡完觉后,会把该整数返回给调用者。我们还会打印出睡眠开始和结束的时间。这有助于我们观察在协程被暂停时,是否有其他代码在并发运行。

为了在后续的代码示例中更方便地引用这个工具函数,我们将创建一个模块,以后在需要时导入。我们还会在这个模块中添加更多可重用的函数。我们把这个模块叫做 util,并将我们的 delay 函数放在一个名为 delay_functions.py 的文件里。我们还会添加一个 __init__.py 文件,里面写上以下这行,以便于漂亮地导入定时器:

from util.delay_functions import delay

从现在起,本书中只要需要用到 delay 函数,我们都会使用 from util import delay。现在我们有了一个可重用的 delay 协程,让我们把它和前面的 add_one 协程结合起来,看看是否能让我们的简单加法运算在 hello_world_message 被暂停的同时并发运行。

import asynciofrom util import delayasync def add_one(number: int) -> int:    return number + 1async def hello_world_message() -> str:    await delay(1)    return 'Hello World!'async def main() -> None:    message = await hello_world_message()    # ❶    one_plus_one = await add_one(1)          # ❷    print(one_plus_one)    print(message)asyncio.run(main())
❶ 暂停  直到  返回。❷ 暂停  直到  返回。

当我们运行这段代码时,1 秒过后,两个函数调用的结果才会被打印出来。我们真正想要的效果是,add_one(1) 的值能在 hello_world_message() 并发运行的同时立即打印出来。那为什么这段代码没有达到这个效果呢?答案是:await 会暂停我们当前的协程,并且不会执行该协程内的任何其他代码,直到 await 表达式给我们返回一个值。由于 hello_world_message 函数需要 1 秒才能返回一个值,因此 main 协程会被暂停 1 秒。在这种情况下,我们的代码表现得就像它是顺序执行的一样。这种行为如图 2.2 所示。

图 2.2 第 2.7 个示例的执行流程

main 和 hello_world 都在等待 delay(1) 完成时暂停了。一旦完成,main 就恢复并可以执行 add_one。

我们希望摆脱这种顺序模型,让 add_one 与 hello_world 并发运行。为了实现这一点,我们需要引入一个叫任务的概念。

最新文章

随机文章