2016-09-08 4 views
4

私は奇妙な発見をしたときにPythonのasyncioを使ってsleep sort(https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort)を実装することに決めました。それは負の値で動作し、0で直ちに戻ります!asyncio.sleepは負の値でどのように機能しますか?

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]次のとおりです。ここで

コードは(あなたがここhttps://repl.it/DYTZそれを実行することができます)です。私はすぐに戻り値0を理解できますが、負の値はどのように正しい順序で戻ってきますか?

+0

[heapq](https://docs.python.org/3.5/library/heapq.html)は、予定時刻を使用してコールバックをソートするために使用されます。つまり、スリープソートは実際には[ヒープソート](https ://en.wikipedia.org/wiki/Heapsort) – Vincent

+0

おかげで、知っておいてよかったです。 – user1475412

答えて

3

まあ、:

  • delay == 0は特殊ケースに入れ、すぐに返すことですが、それもスリープ状態にしようとしません。
  • 0以外の遅延コールevents.get_event_loop()events.set_event_loop_policy(policy)へのコールはasyncio.tasksにありません。すでに他の場所に設定されていない場合は、デフォルト値に戻っているように見えますが、the default is asyncio.DefaultEventLoopPolicyです。
  • WindowsUNIXと異なるため、events.pyで定義されていません。いずれにせよ、sleeploop.create_future()を呼び出す。
  • それはbase_events.BaseEventLoopにあるいくつかの継承を戻して定義しています。これは、Future()コンストラクタに対する単純な呼び出しであり、重要なロジックはありません。ループバックにFutureそのデリゲートのインスタンスから
  • 、次のように:

    future._loop.call_later(delay, 
             futures._set_result_unless_cancelled, 
             future, result) 
    
  • 1はBaseEventLoopでもあり、まだ直接delay数を処理しないこと:それは追加、self.call_atを呼び出します現在の時刻を遅延に加算する。
  • call_atスケジュールを返し、events.TimerHandleを返し、コールバックはFutureに完了したことを伝えることです。戻り値は、タスクを取り消す場合にのみ関連し、クリーンアップのために自動的に終了します。スケジューリングは重要なビットです。
  • _scheduledは、heapqでソートされています。すべてがソート順に並べられ、タイマーは_whenでソートされます。これが鍵です。
  • チェックするたびに、キャンセルされたスケジュールされたすべてのものを取り除き、残りのスケジュールされたコールバックをすべて順番に実行して、準備ができていないコールバックにヒットします。

TL; DR:

は、過去に "準備完了" であると負の期間のスケジュールタスクのasyncioで眠ります。つまり、スケジュールされたタスクのリストの先頭に移動し、イベントループがチェックされるとすぐに実行されます。実際には、スケジューリングされていないために0が最初に来るが、それ以外のものはスケジューラに「遅れて実行中」として登録され、遅い順にすぐに処理されます。

+0

うわー、これは本当に詳細です。ソースを掘り下げてくれてありがとう。 – user1475412

4

asyncioソースを見ると、sleepspecial cases 0がすぐに返されます。

if delay == 0: 
    yield 
    return result 

あなたがソースを続行する場合は、他の値はイベントループのcall_laterメソッドに渡されますことがわかります。どのようにcall_laterがデフォルトループ(BaseEventLoop)に実装されているかを見ると、call_laterpasses a time to call_atが表示されます。

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

値が順番に反転される理由は、負の遅延で作成された時間が正の遅延を持つものよりも前に発生するためです。 source見て

+0

私は見る!たぶん私はすべての値を負にする必要があります:) – user1475412

+0

ガーは、あまりにも長いトレースソースを取って、それをすべて書いて取得し、あなたは私にそれを打つ。 –

+0

ええ、しかし、あなたはヒープにすべて行ったので、あなたは私の上にそれを持っています。 – dirn

関連する問題