乐趣区

关于程序员:Python-异步-常见问题-Part223

动动发财的小手,点个赞吧!

本节答复开发人员在 Python 中应用 asyncio 时提出的常见问题。

6. 正在运行的工作是否会阻止事件循环退出?

不会!

独立调度和运行的工作不会阻止事件循环退出。如果你的主协程没有其余流动要实现并且有独立的工作在后盾运行,你应该检索正在运行的工作并期待它们

7. 如何显示正在运行的工作的进度?

咱们能够在每个工作上应用 done 回调函数来显示进度。实现回调是咱们能够在 asyncio.Task 上注册的函数。

一旦工作实现,它就会被调用,无论是失常还是失败。done 回调函数是一个惯例函数,而不是协程,并将与其关联的 asyncio.Task 作为参数。咱们能够对所有工作应用雷同的回调函数并以通用形式报告进度,例如通过报告音讯。

# callback function to show progress of tasks
def progress(task):
    # report progress of the task
    print('.', end='')

咱们能够在咱们收回的每个 asyncio.Task 上注册一个回调函数。这能够通过在每个工作上应用 add_done_callback() 办法并将回调函数的名称传递给它来实现。

...
# add a done callback to a task
task.add_done_callback(progress)

8. 如何在提早后运行工作?

咱们能够开发一个自定义包装器协程来在提早后执行指标协程。包装协程可能有两个参数,一个协程和一个以秒为单位的工夫。它将在给定的提早距离内休眠(以秒为单位),而后期待提供的协程。上面的 delay() 协程实现了这一点。

# coroutine that will start another coroutine after a delay in seconds
async def delay(coro, seconds):
    # suspend for a time limit in seconds
    await asyncio.sleep(seconds)
    # execute the other coroutine
    await coro

要应用包装协程,能够创立协程对象并间接期待或作为工作独立执行。例如,调用者能够挂起并调度提早协程并期待它实现:

...
# execute a coroutine after a delay
await delay(coro, 10)

或者,调用者能够安顿提早协程独立运行:

...
# execute a coroutine after a delay independently
_ = asyncio.create_task(delay(coro, 10))

9. 如何运行后续工作?

asyncio 中次要有 3 种形式来公布后续工作:

  1. 从已实现的工作自身安顿后续工作。
  2. 安顿呼叫者的后续工作。
  3. 应用实现回调主动安顿后续工作。

实现的工作能够公布本人的后续工作。这可能须要查看一些状态以确定是否应该收回后续工作。而后能够通过调用 asyncio.create_task() 来安顿工作。

...
# schedule a follow-up task
task = asyncio.create_task(followup_task())

工作自身能够抉择期待后续工作,也能够让其在后盾独立实现。

...
# wait for the follow-up task to complete
await task

收回工作的调用者能够抉择收回后续工作。例如,当调用者收回第一个工作时,它可能会保留 asyncio.Task 对象。而后它能够查看工作的后果或工作是否胜利实现。而后调用者能够决定收回后续工作。它可能会也可能不会间接期待后续工作。

...
# issue and await the first task
task = await asyncio.create_task(task())
# check the result of the task
if task.result():
    # issue the follow-up task
    followup = await asyncio.create_task(followup_task())

咱们能够应用 done 回调函数主动执行后续工作。例如,收回工作的调用者能够在工作自身上注册一个实现的回调函数。done 回调函数必须将 asyncio.Task 对象作为参数,并且只有在工作实现后才会被调用。而后它能够抉择公布后续工作。done 回调函数是一个一般的 Python 函数,不是协程,所以不能期待后续工作

例如,回调函数可能如下所示:

# callback function
def callback(task):
    # schedule and await the follow-up task
    _ = asyncio.create_task(followup())

调用者能够收回第一个工作并注册实现的回调函数。

...
# schedule and the task
task = asyncio.create_task(work())
# add the done callback function
task.add_done_callback(callback)

10. 如何执行阻塞 I/O 或 CPU 绑定函数?

asyncio 模块提供了两种在 asyncio 程序中执行阻塞调用的办法。第一种是应用 asyncio.to_thread() 函数。这是在高级 API 中,供给用程序开发人员应用。asyncio.to_thread() 函数采纳要执行的函数名和任何参数。该函数在独自的线程中执行。它返回一个能够作为独立工作期待或安顿的协程。

...
# execute a function in a separate thread
await asyncio.to_thread(task)

在返回的协程有机会在事件循环中运行之前,工作不会开始执行。asyncio.to_thread() 函数在后盾创立一个 ThreadPoolExecutor 来执行阻塞调用。因而,asyncio.to_thread() 函数仅实用于 IO 绑定工作。

另一种办法是应用 loop.run_in_executor() 函数。这是在低级异步 API 中,首先须要拜访事件循环,例如通过 asyncio.get_running_loop() 函数。loop.run_in_executor() 函数承受一个执行器和一个要执行的函数。

如果没有为执行器提供,则应用默认执行器,即 ThreadPoolExecutor。loop.run_in_executor() 函数返回一个可期待对象,如果须要能够期待它。工作将立刻开始执行,因而返回的可期待对象不须要期待或安顿阻塞调用开始执行。

...
# get the event loop
loop = asyncio.get_running_loop()
# execute a function in a separate thread
await loop.run_in_executor(None, task)

或者,能够创立一个执行器并将其传递给 loop.run_in_executor() 函数,该函数将在执行器中执行异步调用。

在这种状况下,调用者必须治理执行器,一旦调用者实现它就将其敞开。

...
# create a process pool
with ProcessPoolExecutor as exe:
    # get the event loop
    loop = asyncio.get_running_loop()
    # execute a function in a separate thread
    await loop.run_in_executor(exe, task)
    # process pool is shutdown automatically...

这两种办法容许阻塞调用作为异步工作在 asyncio 程序中执行。

本文由 mdnice 多平台公布

退出移动版