理解Python的wait c协程(阻塞循环示例)

pxq42qpu  于 6个月前  发布在  Python
关注(0)|答案(2)|浏览(47)

我正在试图理解协程,我知道它们使用的是单线程,而await关键字的意思是“把控制权交还给其他正在运行的协程,并给它们一个运行的机会”。
我举下面的例子:

import asyncio
import time

async def long_function():
    # it takes almost 1 sec to execute it
    for _ in range(50000000):
        pass

async def count():
    for x in range(10):
        print(x)
        #await asyncio.sleep(0)
        await long_function()

async def gather():
    await asyncio.gather(count(), count())

asyncio.run(gather())

字符串
我希望这段代码异步运行,并给予如下输出:

0
0
1
1
2
2
...


然而,它提供了:

0
1
2
3
...
0
1
2
3
...


当我取消asyncio.sleep(0)的注解时,输出与预期的一样-异步。
有谁能解释一下为什么使用await long_function()不足以将控制传递给其他协程吗?它是如何在一些较低的级别上工作的,以帮助我理解。
谢谢

jgwigjjp

jgwigjjp1#

当你取消注解await.sleep(0)时,它会引入一个小的暂停,允许事件循环切换到其他协程。这是因为await.sleep(0)是一种非阻塞的方式,可以将控制权交给事件循环,允许其他任务运行。通过偶尔放弃控制权来使长时间运行的CPU绑定任务合作是一个常见的技巧。
以下是发生的事情的分解:
1.'count'协程运行并打印0。

  1. await long_function()在count中被调用。由于long_function是CPU绑定的,不涉及任何I/O或异步操作,它将运行到完成,而不会将控制权交给事件循环。
    1.当long_function运行时,其他协程(比如另一个计数协程)正在等待轮到它们,但是它们直到long_function完成才能得到它。
  2. uncommenting await algorithm.sleep(0)引入了一个非阻塞睡眠,允许事件循环在long_function执行期间切换到其他协程。
    一般来说,如果你正在处理CPU绑定的任务,并希望实现并行性,你可能会考虑使用“concurrent.futures”或其他多处理技术。
31moq8wy

31moq8wy2#

在阅读这篇有用的文章后:https://bbc.github.io/cloudfit-public-docs/asyncio/asyncio-part-1https://bbc.github.io/cloudfit-public-docs/asyncio/asyncio-part-2
我发现我误解了await关键字的含义,因为这个关键字的行为取决于我们正在等待的对象。有三种类型的awaitable:

  • 协程-当等待时,它会立即添加到执行堆栈,并执行,而不会产生控制
  • Future -当等待时,它取决于状态。如果是FINISHED,则返回结果,如果不是NOT FINISHED,则将控制权交还给循环
  • 任务-继承自Future

所以我的代码没有异步运行的原因是我等待了一个协程,所以它被添加到堆栈并执行。
使用await asyncio.sleep(0)是将控制权交还给asyncio.sleep,因为asyncio.sleep在下面使用Future。
我的原始代码可以重写为异步的,像这样:

import asyncio
import time

async def long_function():
    # it takes almost 1 sec to execute it
    for _ in range(50000000):
        pass

async def count():
    for x in range(10):
        print(x)
        task = asyncio.create_task(long_function())
        # awaiting the task causes the control to be given back
        await task

async def gather():
    await asyncio.gather(count(), count())

asyncio.run(gather())

字符串

相关问题