web-dev-qa-db-ja.com

RuntimeError:async + apschedulerのスレッドには現在のイベントループはありません

非同期機能があり、N分ごとにapschedullerで実行する必要があります。以下にpythonコードがあります

URL_LIST = ['<url1>',
            '<url2>',
            '<url2>',
            ]

def demo_async(urls):
    """Fetch list of web pages asynchronously."""
    loop = asyncio.get_event_loop() # event loop
    future = asyncio.ensure_future(fetch_all(urls)) # tasks to do
    loop.run_until_complete(future) # loop until done

async def fetch_all(urls):
    tasks = [] # dictionary of start times for each url
    async with ClientSession() as session:
        for url in urls:
            task = asyncio.ensure_future(fetch(url, session))
            tasks.append(task) # create list of tasks
        _ = await asyncio.gather(*tasks) # gather task responses

async def fetch(url, session):
    """Fetch a url, using specified ClientSession."""
    async with session.get(url) as response:
        resp = await response.read()
        print(resp)

if __name__ == '__main__':
    scheduler = AsyncIOScheduler()
    scheduler.add_job(demo_async, args=[URL_LIST], trigger='interval', seconds=15)
    scheduler.start()
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))

    # Execution will block here until Ctrl+C (Ctrl+Break on Windows) is pressed.
    try:
        asyncio.get_event_loop().run_forever()
    except (KeyboardInterrupt, SystemExit):
        pass

しかし、実行しようとしたときに次のエラー情報があります

Job "demo_async (trigger: interval[0:00:15], next run at: 2017-10-12 18:21:12 +04)" raised an exception.....
..........\lib\asyncio\events.py", line 584, in get_event_loop
    % threading.current_thread().name)
RuntimeError: There is no current event loop in thread '<concurrent.futures.thread.ThreadPoolExecutor object at 0x0356B150>_0'.

これで私を助けてくれますか? Python 3.6、APScheduler 3.3.1、

22
Valera Shutylev

fetch_allscheduler.add_job()に直接渡すだけです。 asyncioスケジューラは、ジョブターゲットとしてコルーチン機能をサポートしています。

ターゲットcallableがnotコルーチン関数である場合、(歴史的な理由により)ワーカースレッドで実行されるため、例外となります。

5
Alex Grönholm

def demo_async(urls)で、次のものを置き換えてみてください:

loop = asyncio.get_event_loop()

で:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
48
prezha

言及されていない重要なことは、エラーが発生する理由です。個人的には、エラーが発生する理由を知ることは、実際の問題を解決することと同じくらい重要です。

BaseDefaultEventLoopPolicyget_event_loopの実装を見てみましょう。

class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
    ...

    def get_event_loop(self):
        """Get the event loop.

        This may be None or an instance of EventLoop.
        """
        if (self._local._loop is None and
            not self._local._set_called and
            isinstance(threading.current_thread(), threading._MainThread)):
            self.set_event_loop(self.new_event_loop())
        if self._local._loop is None:
            raise RuntimeError('There is no current event loop in thread %r.'
                               % threading.current_thread().name)
        return self._local._loop

self.set_event_loop(self.new_event_loop())は、以下のすべての条件が満たされた場合にのみ実行されることがわかります。

  • self._local._loop is None-_local._loopが設定されていません
  • not self._local._set_called-set_event_loopはまだ呼び出されていません
  • isinstance(threading.current_thread(), threading._MainThread)-現在のスレッドがメインのスレッドです(これはTrueではありません)

したがって、現在のスレッドにループが設定されていないため、例外が発生します。

if self._local._loop is None:
    raise RuntimeError('There is no current event loop in thread %r.'
                       % threading.current_thread().name)
5
Jatimir

この質問は最初のページに引き続き表示されるため、ここに問題と回答を記載します。

flask-socketio および Bleak を使用すると、RuntimeError: There is no current event loop in thread 'Thread-X'.がありました。


Edit:まあ、ファイルをリファクタリングしてクラスを作りました。

コンストラクターでループを初期化すると、すべてが正常に機能するようになりました。

class BLE:
    def __init__(self):
        self.loop = asyncio.get_event_loop()

    # function example, improvement of
    # https://github.com/hbldh/bleak/blob/master/examples/discover.py :
    def list_bluetooth_low_energy(self) -> list:
        async def run() -> list:
            BLElist = []
            devices = await bleak.discover()
            for d in devices:
                BLElist.append(d.name)
            return 'success', BLElist
        return self.loop.run_until_complete(run())

使用法:

ble = path.to.lib.BLE()
list = ble.list_bluetooth_low_energy()

元の答え:

解決策は愚かだった。私は自分のしたことに注意を払っていませんでしたが、次のようにいくつかのimportを関数から外しました。

import asyncio, platform
from bleak import discover

def listBLE() -> dict:
    async def run() -> dict:
        # my code that keep throwing exceptions.

    loop = asyncio.get_event_loop()
    ble_list = loop.run_until_complete(run())
    return ble_list

そのため、コード内の何かを変更する必要があると思い、get_event_loop()の行の直前にこのコードを使用して新しいイベントループを作成しました。

loop = asyncio.new_event_loop()
loop = asyncio.set_event_loop()

ループを実行していたので、この瞬間、私はかなり幸せでした。

しかし、応答しません。そして、私のコードはいくつかの値を返すためにタイムアウトに依存していたので、私のアプリにとってはかなり悪いものでした。

問題がimportであることを理解するのに2時間近くかかりました。ここに私の(動作する)コードがあります:

def list() -> dict:
    import asyncio, platform
    from bleak import discover

    async def run() -> dict:
        # my code running perfectly

    loop = asyncio.get_event_loop()
    ble_list  = loop.run_until_complete(run())
    return ble_list
0
sodimel

イベントループを直接使用する代わりに、asyncio.run()を使用します。新しいループを作成し、終了すると閉じます。

これは、「実行」がどのように見えるかです:

if events._get_running_loop() is not None:
    raise RuntimeError(
        "asyncio.run() cannot be called from a running event loop")

if not coroutines.iscoroutine(main):
    raise ValueError("a coroutine was expected, got {!r}".format(main))

loop = events.new_event_loop()
try:
    events.set_event_loop(loop)
    loop.set_debug(debug)
    return loop.run_until_complete(main)
finally:
    try:
        _cancel_all_tasks(loop)
        loop.run_until_complete(loop.shutdown_asyncgens())
    finally:
        events.set_event_loop(None)
        loop.close()
0
Asaf Pinhassi