如何使包含for循环非阻塞功能?

时间:2019-01-22 15:18:43

标签: python python-asyncio

我正在尝试使以下代码异步:

import asyncio
import random

async def count():
    l = []
    for i in range(10000000):
        l.append(i)
    return random.choice(l)

async def long_task1():
    print('Starting task 1...')
    task_output = await count()
    print('Task 1 output is {}'.format(task_output ))


async def long_task2():
    print('Starting task 2...')
    task_output = await count()
    print('Task 2 output is {}'.format(task_output ))

async def main():
    await asyncio.gather(long_task1(), long_task2())

if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(main())

当前它将同步工作。

是因为count函数缺少await语句吗?

我尝试过对该函数进行重新设计以使其包含await

async def count():
    l = []
    for i in range(10000000):
        l.append(i)
    choice = await random.choice(l)
    return choice

,它将异步启动(Starting task 1...Starting task 2...都将被依次打印),但是随后出现错误:

  

TypeError:对象int不能在“等待”表达式中使用

我知道发生此错误是因为random.choice(l)的结果不是可以等待的(协程),但是我不知道如何解决这个问题而不需要盘旋。我是否需要以某种方式将for循环重构为一个couroutine?

3 个答案:

答案 0 :(得分:3)

  

是因为count函数缺少await语句吗?

简而言之,是的,您已经正确识别了问题。要并行执行任务,不仅需要指定async def,而且还需要等待一些暂停执行的操作,从而将控制权返回到事件循环。在异步中,这通常是一种阻塞同步程序的调用,例如睡眠或尚未准备好读取的套接字读取。

要强制使用temporary suspension,可以在await asyncio.sleep(0)的循环内添加count。在await之类的普通函数之前添加random.choice无效,因为await需要一个实现等待接口的对象,并且在代码random.choice中仅返回整数。

答案 1 :(得分:1)

您的代码将调用同时运行gatherlong_task1的{​​{1}}。然后,您在每个函数中的long_task2上调用await。但这将在该子例程上count完成。因此,整个子例程仍将在下一个子例程开始之前完成。您需要一个函数来暂停整个任务。我创建了两种方法来避免这种情况。两者都涉及创建新任务。

创建一个新的子例程:

await

您还可以使async def count(): l = [] await asyncio.wait_for(loopProcess(l), timeout=1000000.0) return random.choice(l) async def loopProcess(l): for i in range(10000000): l.append(i) 的功能与原始代码相同,并像这样更改count以使long_task(1/2)成为新任务:

count()

如果您使用python 3.7,也可以使用async def long_task1(): print('Starting task 1...') task_output = await asyncio.shield(count()) print('Task 1 output is {}'.format(task_output )) async def long_task2(): print('Starting task 2...') task_output = await asyncio.shield(count()) print('Task 2 output is {}'.format(task_output ))

来源:https://docs.python.org/3/library/asyncio-task.html

答案 2 :(得分:1)

为使asyncio正常工作,在事件循环中不应执行任何占用大量CPU资源的任务(对于循环来说是紧紧的任务)。由于没有办法摆脱for循环。如果在循环中使用显式asyncio.sleep,则只是不必要地进入和退出协程,从而使整个过程变慢。如果您的目标只是看异步如何工作,那很好。

但是在现实世界中,如果您要执行大量的CPU任务,则有两种选择

  1. 使用多处理并将任务委派给其他进程。
  2. 使用可释放GIL并使用线程的本机代码绑定。

顾名思义,该库用于异步io。 async"io"