Naf*_*war 2 python multithreading event-loop python-asyncio
我有一段简单的代码让我发疯了一段时间。几天前我发布了这个问题,询问create_task是否与input. 现在我想出了一些与此相关的事情。我在一个单独的线程中运行事件循环并在其中推送任务。非常直接的代码。
import asyncio
import threading
async def printer(message):
print(f'[printer] {message}')
def loop_runner(loop):
loop.run_forever()
if __name__ == '__main__':
event_loop = asyncio.get_event_loop()
t = threading.Thread(target=loop_runner, args=(event_loop,))
t.start()
for m in ['hello', 'world', 'foo', 'bar']:
print(f'[loop running ?] {event_loop.is_running()}')
event_loop.create_task(printer(m))
Run Code Online (Sandbox Code Playgroud)
除了这些日志消息外,什么都不会打印。
[loop running ?] True
[loop running ?] True
[loop running ?] True
[loop running ?] True
Run Code Online (Sandbox Code Playgroud)
现在,如果我在事件循环线程中阻塞并让它在像这样暂停后运行。
def loop_runner(loop):
time.sleep(1 / 1000)
loop.run_forever()
Run Code Online (Sandbox Code Playgroud)
一切正常,这被打印出来
[loop running ?] False
[loop running ?] False
[loop running ?] False
[loop running ?] False
[printer] hello
[printer] world
[printer] foo
[printer] bar
Run Code Online (Sandbox Code Playgroud)
从表面上看,在运行事件循环中创建的任务似乎没有被执行。但这是为什么呢?
我在文档中没有看到任何关于此的内容。在我在互联网上看到的大多数示例中,人们正在从其他协程循环创建任务并等待它们。但是我认为如果您不想等待它们,在协程之外使用创建任务是合法的。
从事件循环线程外部创建任务时,您需要使用asyncio.run_coroutine_threadsafe. 该函数将以线程安全的方式调度协程,并通知事件循环有新的工作要做。它还将返回一个concurrent.futures.Future对象,您可以使用该对象阻止当前线程,直到结果可用。
从表面上看,在运行事件循环中创建的任务似乎没有被执行。但这是为什么呢?
调用create_task是不够的,因为它不包含“唤醒”事件循环的代码。这是一个特性——通常不需要这样的唤醒,添加它只会减慢常规单线程使用的速度。当create_task从事件循环线程调用时,它位于事件循环回调中,因此事件循环可以在完成执行回调后重新获得控制权时检查其任务队列。但是当create_task从不同的线程调用时,事件循环处于睡眠状态,等待 IO,因此run_coroutine_threadsafe需要将其唤醒。
为了测试这一点,您可以创建一个“心跳”协程,它只包含一个无限循环,可以打印一些东西并等待asyncio.sleep(1)。您将看到使用创建的任务与create_task心跳一起执行,这也恰好唤醒了事件循环。在繁忙的 asyncio 应用程序中,这种效果给人的印象是create_task来自另一个线程“有效”。然而,这绝不应该被依赖,因为create_task无法实现正确的锁定并可能破坏事件循环内部结构。
我在文档中没有看到任何关于此的内容。
看看并发和多线程部分。
| 归档时间: |
|
| 查看次数: |
4991 次 |
| 最近记录: |