乐趣区

关于程序员:Python-异步-常见错误22

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

本节举例说明开发人员在 Python 中应用 asyncio 时遇到的个别谬误。

1. 尝试通过调用协程来运行协程

asyncio 初学者遇到的最常见谬误是像调用函数一样调用协程。

例如,咱们能够应用“async def”表达式定义协程:

# custom coroutine
async def custom_coro():
    print('hi there')

而后初学者将尝试像函数一样调用这个协程,并冀望打印消息被报告。

...
# error attempt at calling a coroutine like a function
custom_coro()

像调用函数一样调用协程不会执行协程体。相同,它将创立一个协程对象。而后能够在 asyncio 运行时中期待该对象,例如事件循环。

咱们能够应用 asyncio.run() 函数启动事件循环来运行协程。

...
# run a coroutine
asyncio.run(custom_coro())

或者,咱们能够暂停以后协程并应用“await”表达式调度另一个协程。

...
# schedule a coroutine
await custom_coro()

2. 不要让协程在事件循环中运行

如果协程未运行,您将收到如下运行时正告:

sys:1: RuntimeWarning: coroutine 'custom_coro' was never awaited

如果您创立协程对象但不安顿它在 asyncio 事件循环中执行,就会产生这种状况。

例如,您可能会尝试从惯例 Python 程序中调用协程:

...
# attempt to call the coroutine
custom_coro()

这不会调用协程。相同,它将创立一个协程对象。

...
# create a coroutine object
coro = custom_coro()

如果您不容许此协程运行,您将收到运行时谬误。正如咱们在上一节中看到的,您能够通过启动异步事件循环并将协程对象传递给它来让协程运行。

...
# create a coroutine object
coro = custom_coro()
# run a coroutine
asyncio.run(coro)

或者,在复合语句的一行中:

...
# run a coroutine
asyncio.run(custom_coro())

如果您在 asyncio 程序中遇到此谬误,那是因为您曾经创立了一个协程并且没有安顿它执行。

这能够应用 await 表达式来实现。

...
# create a coroutine object
coro = custom_coro()
# suspend and allow the other coroutine to run
await coro

或者,您能够安顿它作为工作独立运行。

...
# create a coroutine object
coro = custom_coro()
# schedule the coro to run as a task interdependently
task = asyncio.create_task(coro)

3. 应用低级 Asyncio API

初学者的一个大问题是他们应用了谬误的 asyncio API。这很常见,起因有很多。

  • API 在最新版本的 Python 中产生了很大变动。
  • API 文档页面让事件变得凌乱,显示了两个 API。
  • Web 上其余中央的示例混合应用不同的 API。

应用谬误的 API 会使事件变得更简短(例如更多代码)、更艰难并且更难了解。

Asyncio 提供了两个 API。

  • 面向应用程序开发人员的高级 API(咱们)
  • 面向框架和库开发人员(不是咱们)的低级 API

较低级别的 API 为高级 API 提供了根底,包含事件循环的内部结构、传输协定、策略等。咱们应该简直总是保持应用高级 API。咱们在入门时相对必须保持应用高级 API。咱们有时可能会深入研究低级 API 以实现特定后果。

如果您开始获取事件循环的句柄或应用“循环”变量来做事,那您就错了。通过高级 API 驱动 asyncio 一段时间。开发一些程序。相熟异步编程和随便运行协程。

4. 过早退出主协程

asyncio 程序的一个次要混同点是没有给工作足够的工夫来实现。咱们能够通过 asyncio.create_task() 办法安顿许多协程在 asyncio 程序中独立运行。

主协程是 asyncio 程序的入口点,而后能够继续执行其余流动。如果主协程退出,则 asyncio 程序将终止。

即便有一个或多个协程作为工作独立运行,程序也会终止。这会让你措手不及。

您能够收回许多工作,而后让主协程复原,冀望所有收回的工作都在本人的工夫内实现。相同,如果主协程没有其余事件可做,它应该期待残余的工作。

这能够通过首先通过 asyncio.all_tasks() 函数获取一组所有正在运行的工作,从该汇合中删除本身,而后通过 asyncio.wait() 函数期待残余的工作来实现。

...
# get a set of all running tasks
all_tasks = asyncio.all_tasks()
# get the current tasks
current_task = asyncio.current_task()
# remove the current task from the list of all tasks
all_tasks.remove(current_task)
# suspend until all tasks are completed
await asyncio.wait(all_tasks)

5. 假如竞争条件和死锁是不可能的

并发编程具备特定于并发的故障模式的危险。这包含竞争条件和死锁等问题。

竞争条件波及两个或多个并发单元同时执行雷同的临界区并使资源或数据处于不统一或意外状态。这可能会导致数据损坏和数据失落。

死锁是指并发单元期待永远不会产生的状况,例如资源变得可用。许多 Python 开发人员认为,应用 asyncio 中的协程不可能呈现这些问题。

起因是任何时候在事件循环中只能运行一个协程。确实,一次只能运行一个协程。

问题是,协程能够挂起和复原,并且能够在应用共享资源或共享变量时这样做。如果不爱护要害局部,异步程序中可能会呈现竞争条件。如果不认真治理同步原语,就会产生死锁。

因而,创立 asyncio 程序以确保协程平安是很重要的,协程平安是一个相似于线程平安和过程平安的概念,实用于协程。

本文由 mdnice 多平台公布

退出移动版