web-dev-qa-db-ja.com

Python asyncio:stopメソッドが呼び出されたときにイベントループが停止していないようです

_run_forever_メソッドを使用してPython asyncioイベントループを実行し、すぐに別のスレッドで停止する簡単なテストがあります。ただし、イベントループは表示されません。次のテストケースがあります。

_import asyncio
from threading import Thread

loop = asyncio.get_event_loop()
thread = Thread(target=loop.run_forever)
thread.start()
print('Started!')
loop.stop()
print('Requested stop!')
thread.join()
print('Finished!')
_

このテストケースは次のように出力します。

_Started!
Requested stop!
_

したがって、テストはthread.join()でブロックされ、イベントループが終了するのを待っているようです。

スレッドをダンプすると、次のようになります。

_Thread 0x00007000087ec000 (most recent call first):
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/selectors.py", line 577 in select
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py", line 1388 in _run_once
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py", line 421 in run_forever
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 862 in run
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 914 in _bootstrap_inner
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 882 in _bootstrap

Current thread 0x00007fffc6b273c0 (most recent call first):
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 1070 in _wait_for_tstate_lock
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 1054 in join
_

Pythonコードを詳しく調べていませんが、_selectors.py_は作業を待っているようです。stopを呼び出したため、この問題が発生している可能性があります。イベントループの作業はこれ以上ありませんが、これはかなり問題のある制限である可能性があります。

それとも、これがどのように機能するかについて何か誤解したのでしょうか?

12
Neil

ドキュメント says イベントループクラスについて:

このクラスはスレッドセーフではありません。

そして さらに

イベントループはスレッドで実行され、同じスレッドですべてのコールバックとタスクを実行します。 [...]別のスレッドからのコールバックをスケジュールするには、AbstractEventLoop.call_soon_threadsafe()メソッドを使用する必要があります。例:

loop.call_soon_threadsafe(callback, *args)

私たちが必要としているもののようです:

import asyncio
from threading import Thread

loop = asyncio.get_event_loop()
thread = Thread(target=loop.run_forever)
thread.start()
print('Started!')
loop.call_soon_threadsafe(loop.stop)  # here
print('Requested stop!')
thread.join()
print('Finished!')

プリント:

Started!
Requested stop!
Finished!
14