【问题标题】:How do I set the asyncio event loop for a thread in Python?如何为 Python 中的线程设置异步事件循环?
【发布时间】:2019-02-17 07:35:10
【问题描述】:

我正在尝试创建两个线程,每个线程都有自己的异步事件循环。

我已经尝试了以下代码,但它似乎不起作用:

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

我的用例是调用 Tornado Web 框架的 websocket_connect 异步函数。

【问题讨论】:

  • “它似乎不起作用”是什么意思?你有例外吗?事件循环没有运行吗?
  • 事件循环没有运行,没有例外,但似乎没有在线程中设置事件循环

标签: python python-3.x multithreading python-asyncio


【解决方案1】:

您的线程在各自的事件循环中排队回调,但它们在实际运行事件循环之前退出,因此回调永远不会被执行。此外,您不需要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)

在这种情况下,很少需要拥有多个事件循环线程 - 您通常只创建一个,并允许该线程满足您所有的 asyncio 需求。毕竟,能够在单个事件循环中托管大量任务是 asyncio 的优势之一。

【讨论】:

  • 这个答案帮助我让 Tornado websocket_connect 在它自己的线程和它自己的事件循环中正常工作,感谢这个答案!我错过的重要部分是loop.run_forever(),call_soon_threadsafe 帮助我安排了我想要的功能。谢谢!
  • @RudolfOlah 如果你喜欢call_soon_threadsafe,还可以查看asyncio.run_coroutine_threadsafe,它允许你在事件循环线程中调度一个协程,并返回一个concurrent.futures.Future这允许等待协程完成而不会导致死锁。
  • 感谢您的提示;在我的另一种情况下,使用 Tornado websocket_connect,未来在连接时完成:tornadoweb.org/en/stable/… 我想循环运行该函数直到完成(websocket 客户端已成功连接到服务器)是有意义的,然后运行永远循环。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-16
  • 2020-12-08
相关资源
最近更新 更多