web-dev-qa-db-ja.com

Pythonのスレッドにasyncioイベントループを設定するにはどうすればよいですか?

それぞれ独自のasyncioイベントループを持つ2つのスレッドを作成しようとしています。

次のコードを試しましたが、うまくいかないようです。

import asyncio
from threading import Thread

def hello(thread_name):
    print('hello from thread {}!'.format(thread_name))

event_loop_a = asyncio.new_event_loop()
event_loop_b = asyncio.new_event_loop()

def callback_a():
    asyncio.set_event_loop(event_loop_a)
    asyncio.get_event_loop().call_soon_threadsafe(lambda: hello('a'))

def callback_b():
    asyncio.set_event_loop(event_loop_b)
    asyncio.get_event_loop().call_soon_threadsafe(lambda: hello('b'))

thread_a = Thread(target=callback_a, daemon=True)
thread_b = Thread(target=callback_b, daemon=True)
thread_a.start()
thread_b.start()

私の使用例は、トルネードWebフレームワークのwebsocket_connect非同期関数を呼び出しています。

8
Rudolf Olah

スレッドはそれぞれのイベントループでコールバックをキューイングしていますが、実際にはイベントループを実行する前に終了するため、コールバックは実行されません。また、イベントループが実行されている(または実行される)同じスレッドからコールバックを呼び出すため、 call_soon_threadsafe は必要ありません。

このコードは、予期される出力を出力します。

import asyncio
from threading import Thread

def hello(thread_name):
    print('hello from thread {}!'.format(thread_name))

event_loop_a = asyncio.new_event_loop()
event_loop_b = asyncio.new_event_loop()

def callback_a():
    asyncio.set_event_loop(event_loop_a)
    asyncio.get_event_loop().call_soon(lambda: hello('a'))
    event_loop_a.run_forever()

def callback_b():
    asyncio.set_event_loop(event_loop_b)
    asyncio.get_event_loop().call_soon(lambda: hello('b'))
    event_loop_b.run_forever()

thread_a = Thread(target=callback_a, daemon=True)
thread_b = Thread(target=callback_b, daemon=True)
thread_a.start()
thread_b.start()

call_soon_threadsafeのより一般的な使用例は、あなたが考えていたものとより一致しており、コールバック(または asyncio.run_coroutine_threadsafe を使用するコルーチン)を別のスレッドで実行されているイベントループ。次に例を示します。

import asyncio, threading

def hello(thread_name):
    print('hello from thread {}!'.format(thread_name))

event_loop_a = asyncio.new_event_loop()
event_loop_b = asyncio.new_event_loop()

def run_loop(loop):
    asyncio.set_event_loop(loop)
    loop.run_forever()

threading.Thread(target=lambda: run_loop(event_loop_a)).start()
threading.Thread(target=lambda: run_loop(event_loop_b)).start()

event_loop_a.call_soon_threadsafe(lambda: hello('a'))
event_loop_b.call_soon_threadsafe(lambda: hello('b'))

event_loop_a.call_soon_threadsafe(event_loop_a.stop)
event_loop_b.call_soon_threadsafe(event_loop_b.stop)

その場合、複数のイベントループスレッドが必要になることはめったにありません。通常は1つだけ作成し、そのスレッドがasyncioのすべてのニーズに対応できるようにします。結局のところ、1つのイベントループで多数のタスクをホストできることは、asyncioの強みの1つです。

7
user4815162342