不确定为什么异步任务在事件循环之外完成

时间:2018-09-10 04:57:01

标签: python python-asyncio

我无法弄清楚这种现象。我正在使用Python 3.6.4。

在这里,我有4个异步功能。在test1中,我叫asyncio.ensure_future(TestAsync())并将协程变成一个任务。在其他test2test3test4上,我没有打电话给ensure_future(),我只是创建了协程并将它们传递到列表中,然后将列表传递到事件循环。

我将test1移出了进入事件循环的列表。因此,我的印象是test1无法运行,但是在我的输出中,它确实可以运行。有人可以向我解释为什么test1不在事件循环内时仍然输出吗?

import asyncio
import random

async def TestAsync():
    print("Test 1 started")
    wait = random.randint(1, 10)
    await asyncio.sleep(wait)
    print("Test 1 done awaited: " + str(wait))

async def TestAsync2():
    print("Test 2 started")
    wait = random.randint(1, 10)
    await asyncio.sleep(wait)
    print("Test 2 done awaited: " + str(wait))

async def TestAsync3():
    print("Test 3 started")
    wait = random.randint(1, 10)
    await asyncio.sleep(wait)
    print("Test 3 done awaited: " + str(wait))

async def TestAsync4():
    print("Test 4 started")
    wait = random.randint(1, 10)
    await asyncio.sleep(wait)
    print("Test 4 done awaited: " + str(wait))

test1 = asyncio.ensure_future(TestAsync())
test2 = TestAsync2()
test3 = TestAsync3()
test4 = TestAsync4()
tasklist = [test2, test3, test4]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasklist))

输出

Test 1 started.    <--This is the one wasn't in the event loop
Test 3 started
Test 4 started
Test 2 started
Test 4 done awaited: 1
Test 3 done awaited: 2
Test 1 done awaited: 7
Test 2 done awaited: 10

3 个答案:

答案 0 :(得分:1)

正如其他人指出的那样,ensure_future会将任务添加到默认事件循环中,以在下一次机会运行。 ensure_future将任意等待转换为asyncio.Future,在协程对象的情况下,可通过将其包装到TaskFuture的子类)中来实现,{ 3}}至create_task。无论调用ensure_future的代码是否存储对返回的Future的引用,该事件都会在事件循环下次旋转时运行。

要注意的另一重要事项是run_until_complete(x)的意思是“将x提交到事件循环并运行循环直到x完成”,它并不能防止添加任务之前开始运行对run_until_complete的呼叫。

在Python 3.7中,有一个新函数call,该函数创建一个 new 事件循环并向其提交给定的协程。用asyncio.run_until_complete替换asyncio.run会产生您期望的行为。

答案 1 :(得分:0)

来自the docs for ensure_future

  

安排协程对象的执行

调用id时,协程是计划的,并且有资格在事件循环运行时运行。

答案 2 :(得分:0)

来自docs

  

调用协程不会启动其代码运行-调用返回的协程对象在您安排执行时间之前不会做任何事情。有两种基本方法可以启动它运行:从另一个协程调用await coroutineyield from coroutine(假设另一个协程已经在运行!),或者使用ensure_future()函数或AbstractEventLoop.create_task()方法。

正如@dirn's answer所暗示的那样,ensure_future本身不会启动任务,除非已经运行了事件循环。但是,随后使用run_until_complete进行的循环启动确实会启动任务。