asyncio.sleep如何处理负值?

时间:2016-09-08 18:56:38

标签: python sorting sleep python-asyncio

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

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

2 个答案:

答案 0 :(得分:5)

好吧,看看source

  • ValidTeachers特别容易立即返回,它甚至不会尝试睡觉。
  • 非零延迟电话delay == 0。由于events.get_event_loop()中没有events.set_event_loop_policy(policy)来电,因此除非已将其设置在其他位置the default is asyncio.DefaultEventLoopPolicy,否则它似乎会回到默认状态。
  • asyncio.tasks中未定义此内容,因为WindowsUNIX上的内容有所不同。
  • 无论哪种方式,events.py都会调用sleep。这在base_events.BaseEventLoop中定义了一些遗产。它只是对loop.create_future()构造函数的简单调用,没有重要逻辑。
  • Future()的实例中,它委托回循环,如下所示:

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

<强> TL; DR:

_when睡觉持续时间为负,安排任务准备就绪&#34;在过去。这意味着它们会到达计划任务列表的顶部,并在事件循环检查后立即运行。实际上,0首先是因为它甚至没有安排,但是其他一切都注册到调度程序,因为&#34;运行迟到&#34;并按照有多晚的顺序立即处理。

答案 1 :(得分:4)

如果您查看asyncio来源,sleep special cases 0并立即返回。

if delay == 0:
    yield
    return result

如果您继续浏览源代码,您会发现任何其他值都会传递到事件循环的call_later方法。查看如何为默认循环(call_later)实施BaseEventLoop,您会看到call_later passes a time to call_at

self.call_at(self.time() + delay, callback, *args)

按顺序转换值的原因是负延迟创建的时间发生在具有正延迟的时间之前。