2017-01-21 127 views
1

我有一个关于python的asyncio模块中的事件循环如何管理未完成任务的问题。请看下面的代码:Python asyncio任务排序

import asyncio 

@asyncio.coroutine 
def a(): 
    for i in range(0, 3): 
     print('a.' + str(i)) 
     yield 


@asyncio.coroutine 
def b(): 
    for i in range(0, 3): 
     print('b.' + str(i)) 
     yield 


@asyncio.coroutine 
def c(): 
    for i in range(0, 3): 
     print('c.' + str(i)) 
     yield 


tasks = [ 
    asyncio.Task(a()), 
    asyncio.Task(b()), 
    asyncio.Task(c()), 
] 

loop = asyncio.get_event_loop() 
loop.run_until_complete(asyncio.wait([t1, t2, t3])) 

运行,这将打印:

a.0 
b.0 
c.0 
a.1 
b.1 
c.1 
a.2 
b.2 
c.2 

注意,它总是打印出 '一',那么 'B',那么 'C'。我猜测无论每个协程经过多少次迭代,它总是按照这个顺序打印。所以你永远不会看到类似

b.100 
c.100 
a.100 

从node.js的背景的,这告诉我,事件循环这里是内部保持一个队列,使用它来决定下一步运行哪一个任务。它最初将a()放在队列的前面,然后b(),然后c(),因为这是列表中任务的顺序传递给asyncio.wait()。然后,只要它碰到yield语句,它就会将该任务放在队列的末尾。我想在一个更现实的例子中,假设你正在做一个异步http请求,它会在http响应返回后将a()放回队列末尾。

我可以得到这个阿门吗?

回答

1

当前您的示例不包含任何阻塞I/O代码。试试这个模拟一些任务:

import asyncio 


@asyncio.coroutine 
def coro(tag, delay): 
    for i in range(1, 8): 
     print(tag, i) 
     yield from asyncio.sleep(delay) 


loop = asyncio.get_event_loop() 

print("---- await 0 seconds :-) --- ") 
tasks = [ 
    asyncio.Task(coro("A", 0)), 
    asyncio.Task(coro("B", 0)), 
    asyncio.Task(coro("C", 0)), 
] 

loop.run_until_complete(asyncio.wait(tasks)) 

print("---- simulate some blocking I/O --- ") 
tasks = [ 
    asyncio.Task(coro("A", 0.1)), 
    asyncio.Task(coro("B", 0.3)), 
    asyncio.Task(coro("C", 0.5)), 
] 

loop.run_until_complete(asyncio.wait(tasks)) 

loop.close() 

正如你所看到的,协程是根据需要安排的,而不是按顺序安排的。

+0

对,它会尽快运行回调,因此如果I/O花费不同的时间来完成它们将开始出现故障。我的观点是,如果没有I/O进行(如我的例子),由于对幕后的任务(可能是队列)进行某种管理,它们总是以可预测的顺序运行。而不像线程那样会根据操作系统线程调度器运行它们,并且是不可预测的。 – d512

+0

如果没有I/O,请勿使用asyncio。它们是不可预测的,你不能确定它将如何在不同的实现或版本中或在不同的条件下工作。 – Udi

+0

我很欣赏这种回应,但我的问题不是关于'asyncio'的适当使用,而是它如何实现。当然,对图书馆实施细节做出假设是不安全的,而且我并没有试图暗示以其他方式提出这个问题。只是想看看有没有人知道它是如何工作的。 – d512