How do I set the asyncio event loop for a thread in Python?

為{幸葍}努か 提交于 2021-02-18 20:41:06

问题


I'm trying to create two threads that each have their own asyncio event loop.

I've tried the following code but it doesn't seem to work:

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()

My use case is calling Tornado web framework's websocket_connect async function.


回答1:


Your threads are queuing callbacks in their respective event loops, but they exit before actually running the event loop, so the callbacks never get executed. Also, you don't need call_soon_threadsafe, since you are invoking the callback from the same thread the event loop is (or, rather, will be) running on.

This code prints the expected output:

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()

A more typical use case for call_soon_threadsafe, and more in line with what you might have had in mind, is to submit a callback (or a coroutine using asyncio.run_coroutine_threadsafe) to an event loop running in another thread. Here is an example:

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)

In that case it is rarely needed to have more than one event loop thread - you'd typically create only one, and allow that thread to service all your asyncio needs. After all, being able to host a large number of tasks in a single event loop is one of the strong points of asyncio.



来源:https://stackoverflow.com/questions/52298922/how-do-i-set-the-asyncio-event-loop-for-a-thread-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!