asyncio.sleep如何处理负值?

use*_*412 4 python sorting sleep python-asyncio

当我做出一个奇怪的发现时,我决定使用Python 实现睡眠排序(https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort)asyncio:它使用负值(并立即返回0)!

这是代码(你可以在这里运行https://repl.it/DYTZ):

import asyncio
import random

async def sleepy(value):
    return await asyncio.sleep(value, result=value)


async def main(input_values):
    result = []
    for sleeper in asyncio.as_completed(map(sleepy, input_values)):
        result.append(await sleeper)
    print(result)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    input_values = list(range(-5, 6))
    random.shuffle(input_values)
    loop.run_until_complete(main(input_values))
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,代码需要5秒才能执行,但结果总是如此[0, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5].我可以理解0立即返回,但负值如何以正确的顺序返回?

Dav*_*man 6

好吧,看看来源:

  • delay == 0 是特殊的,立即返回,它甚至没有尝试睡觉.
  • 非零延迟呼叫events.get_event_loop().由于没有调用events.set_event_loop_policy(policy)in asyncio.tasks,它似乎会回退到默认值,除非它已经被设置在其他地方,默认是asyncio.DefaultEventLoopPolicy.
  • 这没有定义events.py,因为它在Windows上与UNIX上不同.
  • 无论哪种方式,sleep电话loop.create_future().这已经定义了一些遗传,过去了base_events.BaseEventLoop.它只是对Future()构造函数的简单调用,没有重要的逻辑.
  • Future它的实例委托回到循环,如下所示:

    future._loop.call_later(delay,
                            futures._set_result_unless_cancelled,
                            future, result)
    
    Run Code Online (Sandbox Code Playgroud)
  • 那个也在BaseEventLoop,但仍然没有直接处理delay数字:它调用self.call_at,将当前时间添加到延迟.
  • call_at调度并返回一个events.TimerHandle,回调就是告诉Future它已完成.返回值仅在要取消任务时才相关,它最终会自动清除.调度是重要的一点.
  • _scheduled排序通过heapq- 一切按顺序排列,计时器按其排序_when.这是关键.
  • 每次检查时,它都会删除所有已取消的计划内容,然后按顺序运行所有剩余的预定回调,直到它找到一个未准备好的回调.

TL; DR:

asyncio在负面持续时间内睡觉安排任务在过去"准备好".这意味着它们会到达计划任务列表的顶部,并在事件循环检查后立即运行.实际上,0首先是因为它甚至没有安排,但是其他所有内容都以"迟到"的形式注册到调度程序,并按照其迟到的顺序立即处理.