8. 使用和查询任务¶
8. Work With and Query Tasks
任务是 asyncio 程序的货币。
在本节中,我们将仔细研究如何在程序中与它们交互。
Tasks are the currency of asyncio programs.
In this section, we will take a closer look at how to interact with them in our programs.
8.1 任务的生命周期¶
8.1 Task Life-Cycle
异步任务有生命周期。
首先,从协程创建任务。
然后它被安排在事件循环内独立执行。
在某个时刻,它会运行。
运行时它可能会被挂起,例如等待另一个协程或任务。 它可能正常完成并返回结果,也可能因异常而失败。
另一个协程可能会干预并取消任务。
最终,它会完成,并且无法再次执行。
我们可以将这个生命周期总结如下:
- 被创建
- 被调度
- 被取消
- 执行
- 挂起
- 执行结果
- 发生异常
- 被取消
- 执行完毕
请注意,暂停、结果、异常和取消本身并不是状态,它们是正在运行的任务的重要转换点。
下图总结了这个生命周期,显示了每个阶段之间的转换。
您可以在教程中了解有关 asyncio 任务生命周期的更多信息:
现在我们已经从高层次上熟悉了任务的生命周期,让我们仔细看看每个阶段。
An asyncio Task has a life cycle.
Firstly, a task is created from a coroutine.
It is then scheduled for independent execution within the event loop.
At some point, it will run.
While running it may be suspended, such as awaiting another coroutine or task. It may finish normally and return a result or fail with an exception.
Another coroutine may intervene and cancel the task.
Eventually, it will be done and cannot be executed again.
We can summarize this life-cycle as follows:
- Created
- Scheduled
- Canceled
- Running
- Suspended
- Result
- Exception
- Canceled
- Done
Note that Suspended, Result, Exception, and Canceled are not states per se, they are important points of transition for a running task.
The diagram below summarizes this life cycle showing the transitions between each phase.
You can learn more about the asyncio task life-cycle in the tutorial:
Now that we are familiar with the life cycle of a task from a high level, let’s take a closer look at each phase.
8.2 如何检查任务的状态¶
8.2 How to Check Task Status
创建任务后,我们可以查看任务的状态。
我们可能想要检查两种状态,它们是:
- 任务是否完成。
- 任务是否被取消。
让我们依次仔细看看每一个。
After a Task is created, we can check the status of the task.
There are two statuses we might want to check, they are:
- Whether the task is done.
- Whether the task was canceled.
Let’s take a closer look at each in turn.
8.2.1 检查任务是否完成¶
8.2.1 Check if a Task is Done
我们可以通过 done() 方法检查任务是否完成。
如果任务完成,该方法返回 True,否则返回 False。
例如:
...
# 检查任务是否完成
if task.done():
# ...
如果任务曾经有机会运行但现在不再运行,则该任务已完成。
已安排的任务未完成。
同样,正在运行的任务也没有完成。
如果满足以下条件,则任务已完成:
- 协程正常结束。
- 协程显式返回。
- 协程中出现意外错误或异常
- 任务被取消。
We can check if a task is done via the done() method.
The method returns True if the task is done, or False otherwise.
For example:
...
# check if a task is done
if task.done():
# ...
A task is done if it has had the opportunity to run and is now no longer running.
A task that has been scheduled is not done.
Similarly, a task that is running is not done.
A task is done if:
- The coroutine finishes normally.
- The coroutine returns explicitly.
- An unexpected error or exception is raised in the coroutine
- The task is canceled.
8.2.2 检查任务是否被取消¶
8.2.2 Check if a Task is Canceled
我们可以通过 cancelled() 方法检查任务是否被取消。
如果任务被取消,该方法返回 True,否则返回 False。
例如:
...
# 检查任务是否被取消
if task.cancelled():
# ...
如果对任务调用 cancel() 方法并成功完成,则任务将被取消,例如 cancel() 返回 True。
如果未调用 cancel() 方法,或者调用 cancel() 方法但取消任务失败,则不会取消任务。
We can check if a task is canceled via the cancelled() method.
The method returns True if the task was canceled, or False otherwise.
For example:
...
# check if a task was canceled
if task.cancelled():
# ...
A task is canceled if the cancel() method was called on the task and completed successfully, e..g cancel() returned True.
A task is not canceled if the cancel() method was not called, or if the cancel() method was called but failed to cancel the task.
8.3 如何获取任务结果¶
8.3 How to Get Task Result
我们可以通过 result() 方法获取任务的结果。
这将返回由 Task 包装的协程的返回值,如果包装的协程未显式返回值,则返回 None。
例如:
...
# 从包装的协程中获取返回值
value = task.result()
如果协程引发未处理的错误或异常,则在调用 result() 方法时会重新引发,并且可能需要处理。
例如:
...
try:
# 从包装的协程中获取返回值
value = task.result()
except Exception:
# 任务失败,没有结果
如果任务被取消,则在调用 result() 方法时会引发 CancelledError 异常,并且可能需要处理。
例如:
...
try:
# 从包装的协程中获取返回值
value = task.result()
except asyncio.CancelledError:
# 任务被取消
因此,最好先检查任务是否被取消。
例如:
...
# 检查任务是否未被取消
if not task.cancelled():
# 从包装的协程中获取返回值
value = task.result()
else:
# 任务被取消
如果任务尚未完成,则在调用 result() 方法时会引发 InvalidStateError 异常,并且可能需要处理。
例如:
...
try:
# 从包装的协程中获取返回值
value = task.result()
except asyncio.InvalidStateError:
# 任务尚未完成
因此,最好先检查任务是否已完成。
例如:
...
# 检查任务是否未完成
if not task.done():
await task
# 从包装的协程中获取返回值
value = task.result()
We can get the result of a task via the result() method.
This returns the return value of the coroutine wrapped by the Task or None if the wrapped coroutine does not explicitly return a value.
For example:
...
# get the return value from the wrapped coroutine
value = task.result()
If the coroutine raises an unhandled error or exception, it is re-raised when calling the result() method and may need to be handled.
For example:
...
try:
# get the return value from the wrapped coroutine
value = task.result()
except Exception:
# task failed and there is no result
If the task was canceled, then a CancelledError exception is raised when calling the result() method and may need to be handled.
For example:
...
try:
# get the return value from the wrapped coroutine
value = task.result()
except asyncio.CancelledError:
# task was canceled
As such, it is a good idea to check if the task was canceled first.
For example:
...
# check if the task was not canceled
if not task.cancelled():
# get the return value from the wrapped coroutine
value = task.result()
else:
# task was canceled
If the task is not yet done, then an InvalidStateError exception is raised when calling the result() method and may need to be handled.
For example:
...
try:
# get the return value from the wrapped coroutine
value = task.result()
except asyncio.InvalidStateError:
# task is not yet done
As such, it is a good idea to check if the task is done first.
For example:
...
# check if the task is not done
if not task.done():
await task
# get the return value from the wrapped coroutine
value = task.result()
8.4 如何获取任务异常¶
8.4 How to Get Task Exception
由任务包装的协程可能会引发未处理的异常。
实际上,这将取消任务。
我们可以通过 Exception()方法在任务包装的协程中检索未处理的异常。
例如:
...
# 获取任务引发的异常
exception = task.exception()
如果包装的协程中未引发未处理的异常,则返回 None 值。
如果任务被取消,则调用 Exception() 方法时会引发 CancelledError 异常,并且可能需要处理。
例如:
...
try:
# 获取任务引发的异常
exception = task.exception()
except asyncio.CancelledError:
# 任务被取消
因此,最好先检查任务是否被取消。
例如:
...
# 检查任务是否未被取消
if not task.cancelled():
# 获取任务引发的异常
exception = task.exception()
else:
# 任务被取消
如果任务尚未完成,则在调用 exception() 方法时会引发 InvalidStateError 异常,并且可能需要进行处理。
例如:
...
try:
# 获取任务引发的异常
exception = task.exception()
except asyncio.InvalidStateError:
# 任务尚未完成
因此,最好先检查任务是否已完成。
例如:
...
# 检查任务是否未完成
if not task.done():
await task
# 获取任务引发的异常
exception = task.exception()
A coroutine wrapped by a task may raise an exception that is not handled.
This will cancel the task, in effect.
We can retrieve an unhandled exception in the coroutine wrapped by a task via the exception() method.
For example:
...
# get the exception raised by a task
exception = task.exception()
If an unhandled exception was not raised in the wrapped coroutine, then a value of None is returned.
If the task was canceled, then a CancelledError exception is raised when calling the exception() method and may need to be handled.
For example:
...
try:
# get the exception raised by a task
exception = task.exception()
except asyncio.CancelledError:
# task was canceled
As such, it is a good idea to check if the task was canceled first.
For example:
...
# check if the task was not canceled
if not task.cancelled():
# get the exception raised by a task
exception = task.exception()
else:
# task was canceled
If the task is not yet done, then an InvalidStateError exception is raised when calling the exception() method and may need to be handled.
For example:
...
try:
# get the exception raised by a task
exception = task.exception()
except asyncio.InvalidStateError:
# task is not yet done
As such, it is a good idea to check if the task is done first.
For example:
...
# check if the task is not done
if not task.done():
await task
# get the exception raised by a task
exception = task.exception()
8.5 如何取消任务¶
8.5 How to Cancel a Task
我们可以通过 cancel() 方法取消计划任务。
如果任务被取消,则取消方法返回 True,否则返回 False。
例如:
...
# 取消任务
was_cancelled = task.cancel()
如果任务已经完成,则无法取消,cancel()方法将返回False,并且任务不会处于已取消状态。
下次任务有机会运行时,它将引发 CancelledError 异常。
如果在包装的协程中未处理 CancelledError 异常,则任务将被取消。
否则,如果在包装的协程中处理 CancelledError 异常,则任务将不会被取消。
cancel() 方法还可以采用消息参数,该参数将在 CancelledError 的内容中使用。
We can cancel a scheduled task via the cancel() method.
The cancel method returns True if the task was canceled, or False otherwise.
For example:
...
# cancel the task
was_cancelled = task.cancel()
If the task is already done, it cannot be canceled and the cancel() method will return False and the task will not have the status of canceled.
The next time the task is given an opportunity to run, it will raise a CancelledError exception.
If the CancelledError exception is not handled within the wrapped coroutine, the task will be canceled.
Otherwise, if the CancelledError exception is handled within the wrapped coroutine, the task will not be canceled.
The cancel() method can also take a message argument which will be used in the content of the CancelledError.
8.6 如何在任务中使用回调¶
8.6 How to Use Callback With a Task
我们可以通过 add_done_callback() 方法向任务添加完成回调函数。
此方法采用任务完成时要调用的函数的名称。
回调函数必须将 Task 实例作为参数。
例如:
# 完成回调函数
def handle(task):
print(task)
...
# 注册完成回调函数
task.add_done_callback(handle)
回想一下,当包装的协程正常完成、返回、引发未处理的异常或取消任务时,任务就可以完成。
add_done_callback() 方法可用于添加或注册任意数量的完成回调函数。
我们还可以通过 remove_done_callback() 函数删除或取消注册回调函数 。
例如:
...
# 删除已完成的回调函数
task.remove_done_callback(handle)
We can add a done callback function to a task via the add_done_callback() method.
This method takes the name of a function to call when the task is done.
The callback function must take the Task instance as an argument.
For example:
# done callback function
def handle(task):
print(task)
...
# register a done callback function
task.add_done_callback(handle)
Recall that a task may be done when the wrapped coroutine finishes normally when it returns, when an unhandled exception is raised or when the task is canceled.
The add_done_callback() method can be used to add or register as many done callback functions as we like.
We can also remove or de-register a callback function via the remove_done_callback() function.
For example:
...
# remove a done callback function
task.remove_done_callback(handle)
8.7 如何设置任务名称¶
8.7 How to Set the Task Name
任务可能有一个名称。
如果从同一个协程创建多个任务并且我们需要某种方法以编程方式区分它们,那么这个名称会很有帮助。
当从协程创建任务时,可以通过 “name” 参数设置名称。
例如:
...
# 从协程创建任务
task = asyncio.create_task(task_coroutine(), name='MyTask')
任务的名称也可以通过 set_name() 方法设置。
例如:
...
# 设置任务名称
task.set_name('MyTask')
我们可以通过 get_name() 方法检索任务的名称。
例如:
...
# 获取任务的名称
name = task.get_name()
您可以在教程中了解有关检查任务状态的更多信息:
A task may have a name.
This name can be helpful if multiple tasks are created from the same coroutine and we need some way to tell them apart programmatically.
The name can be set when the task is created from a coroutine via the “name” argument.
For example:
...
# create a task from a coroutine
task = asyncio.create_task(task_coroutine(), name='MyTask')
The name for the task can also be set via the set_name() method.
For example:
...
# set the name of the task
task.set_name('MyTask')
We can retrieve the name of a task via the get_name() method.
For example:
...
# get the name of a task
name = task.get_name()
You can learn more about checking the status of tasks in the tutorial:
创建日期: 2024年9月4日