跳转至

9. 当前正在运行的任务

9. Current and Running Tasks

我们可以内省在 asyncio 事件循环中运行的任务。

这可以通过获取当前正在运行的任务和所有正在运行的任务的 asyncio.Task 对象来实现。

We can introspect tasks running in the asyncio event loop.

This can be achieved by getting an asyncio.Task object for the currently running task and for all tasks that are running.

9.1 如何获取当前任务

9.1 How to Get the Current Task

我们可以通过 asyncio.current_task() 函数获取当前任务。

此函数将为当前正在运行的任务返回一个 Task 对象。

例如:

...
# 获取当前任务
task = asyncio.current_task()

这将为当前正在运行的任务返回一个 Task 对象。

这可能是:

  • 主协程传递给 asyncio.run()
  • 通过 asyncio.create_task() 在 asyncio 程序中创建和调度的任务。

任务可以创建并运行另一个协程(例如,不包含在任务中)。 从协程中获取当前任务将返回正在运行的任务的 Task 对象,但不是当前正在运行的协程。

如果协程或任务需要有关其自身的详细信息(例如用于记录的任务名称),则获取当前任务会很有帮助。

我们可以探索如何为用于启动 asyncio 程序的主协程获取 Task 实例。

下面的示例定义了一个用作程序入口点的协程。 它报告一条消息,然后获取当前任务并报告其详细信息。

这是一个重要的第一个示例,因为它强调了所有协程都可以作为异步事件循环中的任务进行访问。

下面列出了完整的示例。

# SuperFastPython.com
# 从主协程获取当前任务的示例
import asyncio

# 定义一个主协程
async def main():
    # 报告消息
    print('main coroutine started')
    # 获取当前任务
    task = asyncio.current_task()
    # 报告其详细信息
    print(task)

# 启动异步程序
asyncio.run(main())

运行该示例首先创建主协程并使用它来启动 asyncio 程序。

main() 协程运行并首先报告一条消息。

然后它检索当前任务,这是一个代表其自身(当前正在运行的协程)的 Task 对象。

然后它报告当前正在运行的任务的详细信息。

我们可以看到该任务具有第一个任务的默认名称“Task-1”,并且正在执行 main() 协程,即当前正在运行的协程。

这强调了我们可以使用 asyncio.current_task() 函数来访问当前运行的协程的 Task 对象,该对象自动包装在 Task 对象中。

main coroutine started
<Task pending name='Task-1' coro=<main() running at ...> cb=[_run_until_complete_cb() at ...]>

您可以在教程中了解有关获取当前任务的更多信息:

We can get the current task via the asyncio.current_task() function.

This function will return a Task object for the task that is currently running.

For example:

...
# get the current task
task = asyncio.current_task()

This will return a Task object for the currently running task.

This may be:

  • The main coroutine passed to asyncio.run().
  • A task created and scheduled within the asyncio program via asyncio.create_task().

A task may create and run another coroutine (e.g. not wrapped in a task). Getting the current task from within a coroutine will return a Task object for the running task, but not the coroutine that is currently running.

Getting the current task can be helpful if a coroutine or task requires details about itself, such as the task name for logging.

We can explore how to get a Task instance for the main coroutine used to start an asyncio program.

The example below defines a coroutine used as the entry point into the program. It reports a message, then gets the current task and reports its details.

This is an important first example, as it highlights that all coroutines can be accessed as tasks within the asyncio event loop.

The complete example is listed below.

# SuperFastPython.com
# example of getting the current task from the main coroutine
import asyncio

# define a main coroutine
async def main():
    # report a message
    print('main coroutine started')
    # get the current task
    task = asyncio.current_task()
    # report its details
    print(task)

# start the asyncio program
asyncio.run(main())

Running the example first creates the main coroutine and uses it to start the asyncio program.

The main() coroutine runs and first reports a message.

It then retrieves the current task, which is a Task object that represents itself, the currently running coroutine.

It then reports the details of the currently running task.

We can see that the task has the default name for the first task, ‘Task-1‘ and is executing the main() coroutine, the currently running coroutine.

This highlights that we can use the asyncio.current_task() function to access a Task object for the currently running coroutine, that is automatically wrapped in a Task object.

main coroutine started
<Task pending name='Task-1' coro=<main() running at ...> cb=[_run_until_complete_cb() at ...]>

You can learn more about getting the current task in the tutorial:

9.2 如何获取所有任务

9.2 How to Get All Tasks

我们可能需要访问 asyncio 程序中的所有任务。

这可能有多种原因,例如:

  • 反思程序的当前状态或复杂性。
  • 记录所有正在运行的任务的详细信息。
  • 查找可查询或取消的任务。

我们可以通过 asyncio.all_tasks() 函数获取 asyncio 程序中所有已计划和正在运行(尚未完成)的任务。

例如:

...
# 获取所有任务
tasks = asyncio.all_tasks()

这将返回 asyncio 程序中所有任务的集合。

它是一个集合,因此每个任务仅代表一次。

如果满足以下条件,则将包含任务:

  • 任务已安排但尚未运行。
  • 任务当前正在运行(例如,但当前已暂停)

该集合还将包括当前正在运行的任务的任务,例如 正在执行调用 asyncio.all_tasks() 函数的协程的任务。

另外,请记住,用于启动 asyncio 程序的 asyncio.run() 方法会将提供的协程包装在任务中。 这意味着所有任务的集合将包括程序入口点的任务。

我们可以探索异步程序中有许多任务的情况,然后获取所有任务的集合。

在此示例中,我们首先创建 10 个任务,每个任务都包装并运行相同的协程。

然后,主协程获取程序中计划或运行的所有任务的集合并报告其详细信息。

下面列出了完整的示例。

# SuperFastPython.com
# 启动多个任务并访问所有任务的示例
import asyncio

# 任务的协程
async def task_coroutine(value):
    # 报告消息
    print(f'task {value} is running')
    # 暂时阻塞
    await asyncio.sleep(1)

# 定义一个主协程
async def main():
    # 报告消息
    print('main coroutine started')
    # 启动许多任务
    started_tasks = [asyncio.create_task(task_coroutine(i)) for i in range(10)]
    # 允许某些任务有时间开始
    await asyncio.sleep(0.1)
    # 获取所有任务
    tasks = asyncio.all_tasks()
    # 报告所有任务
    for task in tasks:
        print(f'> {task.get_name()}, {task.get_coro()}')
    # 等待所有任务完成
    for task in started_tasks:
        await task

# 启动异步程序
asyncio.run(main())

运行该示例首先创建主协程并使用它来启动 asyncio 程序。

main() 协程运行并首先报告一条消息。

然后它创建并安排 10 个包装自定义协程的任务,

然后,main() 协程会阻塞一段时间,以允许任务开始运行。

任务开始运行,每个任务报告一条消息,然后休眠。

main() 协程恢复并获取程序中所有任务的列表。

然后它报告每个协程的名称和协程。

最后,它枚举已创建的任务列表并等待每个任务,以允许它们完成。

这强调了我们可以获取 asyncio 程序中所有任务的集合,其中包括创建的任务以及代表程序入口点的任务。

main coroutine started
task 0 is running
task 1 is running
task 2 is running
task 3 is running
task 4 is running
task 5 is running
task 6 is running
task 7 is running
task 8 is running
task 9 is running
> Task-9, <coroutine object task_coroutine at 0x10e186e30>
> Task-2, <coroutine object task_coroutine at 0x10e184e40>
> Task-11, <coroutine object task_coroutine at 0x10e186f10>
> Task-7, <coroutine object task_coroutine at 0x10e186d50>
> Task-4, <coroutine object task_coroutine at 0x10e185700>
> Task-10, <coroutine object task_coroutine at 0x10e186ea0>
> Task-8, <coroutine object task_coroutine at 0x10e186dc0>
> Task-5, <coroutine object task_coroutine at 0x10e186ab0>
> Task-1, <coroutine object main at 0x10e1847b0>
> Task-3, <coroutine object task_coroutine at 0x10e184f90>
> Task-6, <coroutine object task_coroutine at 0x10e186ce0>

您可以了解有关获取所有任务的更多信息。 在教程中:

接下来,我们将探讨如何同时运行多个协程。

We may need to get access to all tasks in an asyncio program.

This may be for many reasons, such as:

  • To introspect the current status or complexity of the program.
  • To log the details of all running tasks.
  • To find a task that can be queried or canceled.

We can get a set of all scheduled and running (not yet done) tasks in an asyncio program via the asyncio.all_tasks() function.

For example:

...
# get all tasks
tasks = asyncio.all_tasks()

This will return a set of all tasks in the asyncio program.

It is a set so that each task is only represented once.

A task will be included if:

  • The task has been scheduled but is not yet running.
  • The task is currently running (e.g. but is currently suspended)

The set will also include a task for the currently running task, e.g. the task that is executing the coroutine that calls the asyncio.all_tasks() function.

Also, recall that the asyncio.run() method that is used to start an asyncio program will wrap the provided coroutine in a task. This means that the set of all tasks will include the task for the entry point of the program.

We can explore the case where we have many tasks within an asyncio program and then get a set of all tasks.

In this example, we first create 10 tasks, each wrapping and running the same coroutine.

The main coroutine then gets a set of all tasks scheduled or running in the program and reports their details.

The complete example is listed below.

# SuperFastPython.com
# example of starting many tasks and getting access to all tasks
import asyncio

# coroutine for a task
async def task_coroutine(value):
    # report a message
    print(f'task {value} is running')
    # block for a moment
    await asyncio.sleep(1)

# define a main coroutine
async def main():
    # report a message
    print('main coroutine started')
    # start many tasks
    started_tasks = [asyncio.create_task(task_coroutine(i)) for i in range(10)]
    # allow some of the tasks time to start
    await asyncio.sleep(0.1)
    # get all tasks
    tasks = asyncio.all_tasks()
    # report all tasks
    for task in tasks:
        print(f'> {task.get_name()}, {task.get_coro()}')
    # wait for all tasks to complete
    for task in started_tasks:
        await task

# start the asyncio program
asyncio.run(main())

Running the example first creates the main coroutine and uses it to start the asyncio program.

The main() coroutine runs and first reports a message.

It then creates and schedules 10 tasks that wrap the custom coroutine,

The main() coroutine then blocks for a moment to allow the tasks to begin running.

The tasks start running and each reports a message and then sleeps.

The main() coroutine resumes and gets a list of all tasks in the program.

It then reports the name and coroutine of each.

Finally, it enumerates the list of tasks that were created and awaits each, allowing them to be completed.

This highlights that we can get a set of all tasks in an asyncio program that includes both the tasks that were created as well as the task that represents the entry point into the program.

main coroutine started
task 0 is running
task 1 is running
task 2 is running
task 3 is running
task 4 is running
task 5 is running
task 6 is running
task 7 is running
task 8 is running
task 9 is running
> Task-9, <coroutine object task_coroutine at 0x10e186e30>
> Task-2, <coroutine object task_coroutine at 0x10e184e40>
> Task-11, <coroutine object task_coroutine at 0x10e186f10>
> Task-7, <coroutine object task_coroutine at 0x10e186d50>
> Task-4, <coroutine object task_coroutine at 0x10e185700>
> Task-10, <coroutine object task_coroutine at 0x10e186ea0>
> Task-8, <coroutine object task_coroutine at 0x10e186dc0>
> Task-5, <coroutine object task_coroutine at 0x10e186ab0>
> Task-1, <coroutine object main at 0x10e1847b0>
> Task-3, <coroutine object task_coroutine at 0x10e184f90>
> Task-6, <coroutine object task_coroutine at 0x10e186ce0>

You can learn more about getting all tasks. in the tutorial:

Next, we will explore how to run many coroutines concurrently.


最后更新: 2024年9月4日
创建日期: 2024年9月4日