asyncio.sleep 如何处理负值?
How does asyncio.sleep work with negative values?
我决定使用 Python 的 asyncio
实现睡眠排序 (https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort),当时我有一个奇怪的发现:它适用于负值(和 returns立即与 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 立即返回,但是负值如何以正确的顺序返回?
如果您查看 asyncio 源代码,sleep
special cases 0 和 returns 立即。
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)
值按顺序翻转的原因是使用负延迟创建的时间出现在使用正延迟的时间之前。
嗯,看看 source:
delay == 0
是 return 的特殊情况,它甚至不会尝试休眠。
- 非零延迟调用
events.get_event_loop()
。由于 asyncio.tasks
中没有对 events.set_event_loop_policy(policy)
的调用,它似乎会退回到默认设置,除非它已经在其他地方设置,并且 the default is asyncio.DefaultEventLoopPolicy
.
- 这在
events.py
中没有定义,因为它在 Windows from on UNIX 上有所不同。
- 无论如何,
sleep
调用 loop.create_future()
。在 base_events.BaseEventLoop
中定义了一些继承。这只是对 Future()
构造函数的简单调用,没有重要的逻辑。
它从 Future
的实例委托回循环,如下所示:
future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
- 那个也在
BaseEventLoop
中,但仍然没有直接处理delay
号码:它调用self.call_at
,将当前时间添加到延迟中。
call_at
计划和 return 一个 events.TimerHandle
,回调是告诉 Future
它已经完成。 return 值仅在要取消任务时才相关,它会自动在结束时进行清理。调度是重要的一点。
_scheduled
通过 heapq
排序 - 一切都按排序顺序进行,计时器按其 _when
排序。这是关键。
- 每次检查时,它都会删除所有已取消的预定事件,然后 运行 按顺序删除所有剩余的预定回调,直到遇到未准备好的回调。
TL;DR:
与 asyncio
一起睡负持续时间将任务安排在过去 "ready"。这意味着它们会到达计划任务列表的顶部,并且一旦事件循环检查就 运行 。实际上,0 排在第一位,因为它甚至不进行调度,但其他所有内容都会以 "running late" 的形式注册到调度程序,并按照迟到的顺序立即处理。
我决定使用 Python 的 asyncio
实现睡眠排序 (https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort),当时我有一个奇怪的发现:它适用于负值(和 returns立即与 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 立即返回,但是负值如何以正确的顺序返回?
如果您查看 asyncio 源代码,sleep
special cases 0 和 returns 立即。
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)
值按顺序翻转的原因是使用负延迟创建的时间出现在使用正延迟的时间之前。
嗯,看看 source:
delay == 0
是 return 的特殊情况,它甚至不会尝试休眠。- 非零延迟调用
events.get_event_loop()
。由于asyncio.tasks
中没有对events.set_event_loop_policy(policy)
的调用,它似乎会退回到默认设置,除非它已经在其他地方设置,并且 the default isasyncio.DefaultEventLoopPolicy
. - 这在
events.py
中没有定义,因为它在 Windows from on UNIX 上有所不同。 - 无论如何,
sleep
调用loop.create_future()
。在base_events.BaseEventLoop
中定义了一些继承。这只是对Future()
构造函数的简单调用,没有重要的逻辑。 它从
Future
的实例委托回循环,如下所示:future._loop.call_later(delay, futures._set_result_unless_cancelled, future, result)
- 那个也在
BaseEventLoop
中,但仍然没有直接处理delay
号码:它调用self.call_at
,将当前时间添加到延迟中。 call_at
计划和 return 一个events.TimerHandle
,回调是告诉Future
它已经完成。 return 值仅在要取消任务时才相关,它会自动在结束时进行清理。调度是重要的一点。_scheduled
通过heapq
排序 - 一切都按排序顺序进行,计时器按其_when
排序。这是关键。- 每次检查时,它都会删除所有已取消的预定事件,然后 运行 按顺序删除所有剩余的预定回调,直到遇到未准备好的回调。
TL;DR:
与 asyncio
一起睡负持续时间将任务安排在过去 "ready"。这意味着它们会到达计划任务列表的顶部,并且一旦事件循环检查就 运行 。实际上,0 排在第一位,因为它甚至不进行调度,但其他所有内容都会以 "running late" 的形式注册到调度程序,并按照迟到的顺序立即处理。