2

コルーチンデコレータとキーワードからの利回りを使用せずに、関数内で asyncio 制御ループを一定期間解放する方法があるかどうか疑問に思っていますか?

import asyncio
import time


class MyClass(object):

    def do_something_periodically(self, delay, repeats):
        for i in range(repeats):
            # Do something useful
            self._sleep(delay)

    def _sleep(self, delay):
        time.sleep(delay)


class MyAsyncioClass(MyClass):

    def _sleep(self, delay):
        # Perform an asyncio.sleep(delay) here which yields control of the event loop
        # and waits for time delay before returning


loop = asyncio.get_event_loop()
obj1 = MyAsyncioClass()
obj2 = MyAsyncioClass()
loop.run_until_complete(asyncio.wait(
    [obj1.do_something_periodically(1000, 3),
     obj2.do_something_periodically(2000, 2)]))

asyncio について何も知らないコードから do_something_periodically メソッドを呼び出すことができるように、これを実行できるようにしたいと考えていますが、スリープ中にループ制御を解放します。これは可能ですか?

ありがとう!

私の特定のユースケースのカットダウンバージョンを表示するように編集されました

4

2 に答える 2

6

これはうまくいく方法ではありませんasyncio明示的な非同期モデルを使用します。コードが制御をイベント ループに戻す場合は、 を使用するかyield from、 callbacks/ を使用する必要がありますFutures。関数 ( など) の内部にいる場合は、 1) を使用して2) メソッドを完全に終了do_something_periodicallyしないと、イベント ループに制御を戻すことはできません。クラスのおよび 非バージョンでyield fromある程度のコードを再利用できますが、 を呼び出す必要があるメソッドは、それ自体も である必要があります。asyncioasynciocoroutinecoroutine

class MyClass(object):

    def do_something_periodically(self, delay, repeats):
        for i in range(repeats):
            self.do_something_useful()
            self._sleep(delay)

    def _sleep(self, delay):
        time.sleep(delay)

    def do_something_useful(self):
        # Do something useful here, which doesn't need to yield to the event loop

class MyAsyncioClass(MyClass):

    @asyncio.coroutine
    def do_something_periodically(self, delay, repeats):
        for i in range(repeats):
            self.do_something_useful()
            yield from self._sleep(delay)

    @asyncio.coroutine
    def _sleep(self, delay):
        yield from asyncio.sleep(delay)

そうは言っても、あなたの特定のユースケースは別の方法で解決できる可能性があるように見えますが、少し見苦しく、MyClassロジックを変更する必要があります。

class MyClass(object):

    def do_something_periodically(self, delay, repeats, i=0):
        while i < repeats:
            # do something useful
            if not self._sleep(delay, repeats, i):
                break
            i+= 1
        return i

    def _sleep(self, delay, repeats, i):
        time.sleep(delay)
        return True

class MyAsyncioClass(MyClass):

    def do_something_periodically(self, delay, repeats, i=0):
        out = super().do_something_periodically(delay, repeats, i)
        if out == repeats:
            asyncio.get_event_loop().stop()

    def _sleep(self, delay, repeats, i):
        i+=1
        asyncio.get_event_loop().call_later(delay, 
                                            self.do_something_periodically, 
                                            delay, repeats, i)
        return False

通常のユースケースのループを完全に反復するだけでなく、その場合に値を増やして繰り返し呼び出されることもサポートするために、 とloop.call_later同等の操作を行います。asyncio.sleepdo_something_periodicallywhileiasyncio

asyncio残念ながら、同期とユースケースの両方で同じコードを再利用する簡単で確実な方法はありません。asyncioこれは、 /のような明示的な非同期フレームワークと、暗黙の非同期モデルを使用する のtornadoようなものの主な欠点の 1 つです。geventではgevent、が完了するまでイベント ループに制御を戻すバージョンtime.sleep(delay)のパッチが適用されます。つまり、コードの変更は必要ありません。geventsleep

于 2014-10-21T16:57:33.337 に答える