web-dev-qa-db-ja.com

ローリングコルーチンが永久に実行されるイベントループを作成する方法は?

コンテキストの切り替えを防ぐために、ネットワーク接続といくつかのルーチンの両方を処理する大きなループを作成します。

通常の機能の実装は次のとおりです。

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)

try:
    # Blocking call interrupted by loop.stop()
    print('step: loop.run_forever()')
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

コルーチンの実装は次のとおりです。

import asyncio


@asyncio.coroutine
def hello_world():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World')

@asyncio.coroutine
def good_evening():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world(),
        good_evening()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

そして混合物:

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

@asyncio.coroutine
def hello_world_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World Coroutine')

@asyncio.coroutine
def good_evening_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening Coroutine')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
print('step: asyncio.async(hello_world_coroutine)')
asyncio.async(hello_world_coroutine())
print('step: asyncio.async(good_evening_coroutine)')
asyncio.async(good_evening_coroutine())

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

ご覧のとおり、各コルーチン関数にはwhileループが囲まれています。どうすれば通常のようにできますか?つまり完了したら、指定された遅延時間の後に自分自身を呼び出しますが、ループを配置するだけではありません。

19
changyuheng

あなたが本当にコルーチンからwhileループを排除したい場合(なぜあなたはそれが必要だと思うのか分かりません;それはあなたがやろうとしていることを行うための最も自然な方法です)、あなたは _asyncio.async_ (または_asyncio.ensure_future_ on Python 3.4.4+)で、次のイベントループの反復でコルーチンを再度実行するようにスケジュールします。

_import asyncio

@asyncio.coroutine
def hello_world():
    yield from asyncio.sleep(1)
    print('Hello World')
    asyncio.async(hello_world())

@asyncio.coroutine
def good_evening():
    yield from asyncio.sleep(1)
    print('Good Evening')
    asyncio.async(good_evening())

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    asyncio.async(hello_world())
    asyncio.async(good_evening())
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()
_

_hello_world_/_good_evening_は印刷後すぐに終了するため、これを行う場合はloop.run_forever()を使用するように切り替える必要があることに注意してください。

18
dano
# asyncio_coroutine_forever.py

import asyncio

async def hello_world():

    await asyncio.sleep(1)
    print('Hello World')
    await good_evening()


async def good_evening():

    await asyncio.sleep(1)
    print('Good Evening')
    await hello_world()


loop = asyncio.get_event_loop()

try:

    loop.run_until_complete(hello_world())
    loop.run_until_complete(good_evening())
    loop.run_forever()

finally:

    print('closing event loop')
    loop.close()
3
Down the Stream
import asyncio


@asyncio.coroutine
def hello_world_coroutine():
    yield from asyncio.sleep(1)
    print('Hello World Coroutine')
    yield from hello_world_coroutine()

@asyncio.coroutine
def good_evening_coroutine():
    yield from asyncio.sleep(1)
    print('Good Evening Coroutine')
    yield from good_evening_coroutine()

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world_coroutine(),
        good_evening_coroutine()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

[〜#〜] upd [〜#〜]

このコードは、最大再帰深度に達します。 Pythonにはテールコールの最適化がないため、ここのコードは間違った例として残してください。

1
changyuheng

あなたは実際にあなたが与えた3つの例を実行しようとしましたか?動作の違いは非常に明白です。

期待したことを言ったことがないので、何が正しいのか、何がそうでないのかを伝えることはありません。 3つの実装はすべて正しいか間違っている可能性があります。各実装にどのような動作があり、なぜそのような動作があるのか​​を説明できます。あなただけが正しいかどうかを知っています。


2番目の例(yield from asyncio.sleep(1))では、2つのコルーチンが同時に実行されます。これは、それぞれが独自に実行することを意味します。 hello_worldは毎秒Hello Worldを出力し、good_eveningは毎秒Good Eveningを出力します。

他の2つの例は両方ともtime.sleep(1)を使用していますが、これはブロッキングです。つまり、最初の関数(つまり、hello_world)がtime.sleep(1)に達すると、プログラム全体が1秒間ハングするになります。つまり、hello_worldがスリープすると、good_eveningも実行できず、その逆も同様です。

プログラムは次のように実行されます。

  1. ループに入ります。
  2. ループはhello_worldを呼び出します。
  3. hello_worldtime.sleep(1)に達しました。プログラムは1秒間スリープします。
  4. Hello Worldが印刷されました。
  5. hello_worldは降伏します。
  6. ループはgood_eveningを呼び出します。
  7. Good Eveningが印刷されました。
  8. good_eveningtime.sleep(1)に達しました。プログラムは1秒間スリープします。
  9. good_eveningは降伏します。
  10. 2に進みます。

したがって、Hello WorldGood Eveningは両方ともtwo秒ごとに表示されます。これは、各printの間に2つのtime.sleep(1)呼び出しがあるためです。 2つの例を実行すると、簡単にわかります。

0
uranusjr