关于 RuntimeWarning: coroutine 'XXX' was never awaited 报错:3.11新版本改动

3,193 阅读1分钟

问题

代码如下:

import asyncio
import time

async def func1():
    print('这里是func1-1')
    await asyncio.sleep(3)
    print('这里是func1-2')
async def func2():
    print('这里是func2-1')
    await asyncio.sleep(2)
    print('这里是func2-2')
async def func3():
    print('这里是func3-1')
    await asyncio.sleep(4)
    print('这里是func3-2')

async def main():
    f1 = func1()
    f2 = func2()
    f3 = func3()
    tasks = [
        f1,f2,f3
    ]
    await asyncio.wait(tasks)

if __name__ == '__main__':
    t1 = time.time()
    asyncio.run(main())
    t2 = time.time()
    print(t2-t1)

报错:

image.png 简单翻译一下: 禁止将协程对象直接传递给 wait()

这是因为python 3.11版本的新特性

解决

方法一:改动main()如下:

async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(func1())
        task2 = tg.create_task(func2())
        task3 = tg.create_task(func3())

运行结果:

image.png

方法二:

将with改成gather: asyncio.gather方法,与asyncio.wait方法类似,但更强大。如果列表中传入的不是create_task方法创建的协程任务,它会自动将函数封装成协程任务

`async def main():
    f1 = func1()
    f2 = func2()
    f3 = func3()
    tasks = [
        f1,f2,f3
    ]
    await asyncio.gather(*tasks)`

3.11 改动文档

下面附上3.11版本的改动示例,以作参考:

3.11版本以前写法:

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")

    await say_after(1, 'hello')
    await say_after(2, 'world')

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

3.11版本写法:

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")

3.11版本的替代简便写法:(官方推荐)

async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(
            say_after(1, 'hello'))

        task2 = tg.create_task(
            say_after(2, 'world'))

        print(f"started at {time.strftime('%X')}")

    # The await is implicit when the context manager exits.

    print(f"finished at {time.strftime('%X')}")