如何在Python中交替运行多个异步函数而不休眠?

bnlyeluc  于 2023-02-10  发布在  Python
关注(0)|答案(2)|浏览(183)

当使用下面的await asyncio.sleep(1)运行代码时:

import asyncio

async def test1():
    for _ in range(0, 3):
        print("Test1")
        await asyncio.sleep(1) # Here
        
async def test2():
    for _ in range(0, 3):
        print("Test2")
        await asyncio.sleep(1) # Here
        
async def test3():
    for _ in range(0, 3):
        print("Test3")
        await asyncio.sleep(1) # Here

async def call_tests():
    await asyncio.gather(test1(), test2(), test3())

asyncio.run(call_tests())

test1()test2()test3()交替运行,每次休眠1秒,如下所示:

Test1
Test2
Test3
Test1
Test2
Test3
Test1
Test2
Test3

现在,我想不睡觉地交替运行它们,但如果我从它们中删除await asyncio.sleep(1)

# ...

async def test1():
    for _ in range(0, 3):
        print("Test1")
        # await asyncio.sleep(1)
        
async def test2():
    for _ in range(0, 3):
        print("Test2")
        # await asyncio.sleep(1)
        
async def test3():
    for _ in range(0, 3):
        print("Test3")
        # await asyncio.sleep(1)

# ...

它们按顺序运行,如下所示:

Test1
Test1
Test1
Test2
Test2
Test2
Test3
Test3
Test3

那么,我怎样才能不睡觉地交替运行它们呢?

iqxoj9l9

iqxoj9l91#

正如不同的人在评论中所说的那样,实现这一点的方法是调用await asyncio.sleep(0),但重要的不是sleep调用:如果值为0,则不会暂停,如果事件循环中没有挂起的任务,则会立即恢复代码。
真正重要的是,必须通过等待协同例程或其他可等待对象的方式,显式地将控制权暂时返回给事件循环。如果没有await <xxx>语句,代码执行永远不会转移到事件循环,异步执行模型也无法运行其他任务中的任何代码:该模型是单线程的,它所做的是让异步循环在任务之间交替执行。等待某个东西是将控制传递给事件循环的方式。现在,如果你不是真的在等待某个异步函数完成,而只是想将控制传递给事件循环,给予其他任务有机会运行,没有特定的调用--所以使用asyncio.sleep(0)甚至 * 在Python标准库内的 * 官方 * 异步代码中使用它。例如:https://github.com/python/cpython/blob/main/Lib/asyncio/streams.py#L376
当你在做这个的时候,检查实际的asyncio.sleep实现也同样简单,可以看到0和负值不会导致任何休眠:只需要让控制流到asyncio.loop:https://github.com/python/cpython/blob/1438b779971605e516bd0a4051a704d6ffbbd58d/Lib/asyncio/tasks.py#L630

qq24tv8q

qq24tv8q2#

休眠0秒的await asyncio.sleep(0)可以不休眠交替运行,如下所示:

import asyncio

async def test1():
    for _ in range(0, 3):
        print("Test1")
        await asyncio.sleep(0) # Here
        
async def test2():
    for _ in range(0, 3):
        print("Test2")
        await asyncio.sleep(0) # Here
        
async def test3():
    for _ in range(0, 3):
        print("Test3")
        await asyncio.sleep(0) # Here

async def call_tests():
    await asyncio.gather(test1(), test2(), test3())

asyncio.run(call_tests())

这就是结果:

Test1
Test2
Test3
Test1
Test2
Test3
Test1
Test2
Test3

相关问题