【发布时间】:2020-12-21 13:00:03
【问题描述】:
我想同时异步运行阻塞和解除阻塞任务。显然,有必要使用 run_in_executor 方法来阻止来自 asyncio 的任务。这是我的示例代码:
import asyncio
import concurrent.futures
import datetime
import time
def blocking():
print("Enter to blocking()", datetime.datetime.now().time())
time.sleep(2)
print("Exited from blocking()", datetime.datetime.now().time())
async def waiter():
print("Enter to waiter()", datetime.datetime.now().time())
await asyncio.sleep(3)
print("Exit from waiter()", datetime.datetime.now().time())
async def asynchronous(loop):
print("Create tasks", datetime.datetime.now().time())
task_1 = asyncio.create_task(waiter())
executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)
task_2 = loop.run_in_executor(executor, blocking)
tasks = [task_1, task_2]
print("Tasks are created", datetime.datetime.now().time())
await asyncio.wait(tasks)
if __name__ == "__main__":
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(asynchronous(loop))
except (OSError) as exc:
sys.exit('Exception: ' + str(exc))
我应该使用相同的事件循环来阻塞run_in_executor中的任务,还是必须使用另一个?我应该在我的代码中进行哪些更改以使其异步工作?谢谢
【问题讨论】:
-
除了答案所说的之外,请注意您不需要为每次运行创建新的线程池。线程池的重点是重用线程以提高效率。您可以简单地将
None作为第一个参数传递给run_in_executor,并让它使用由 asyncio 创建的线程池进行事件循环。 -
哦,我明白了。但是如果我不使用自定义线程拉取的单独线程,它会阻塞带有事件循环的线程,不是吗?
-
我不是说你应该使用线程池,我只是说你不需要创建自己的线程池。将
None传递给run_in_executor将使用 asyncio 自己的线程池,该线程池就是为此目的而存在的。该线程池仍然有自己的线程,不会阻塞事件池线程。 -
我明白了,现在。我只是对官方文档中的这句话感到困惑:“loop.run_in_executor() 方法可以与 concurrent.futures.ThreadPoolExecutor 一起使用,以在不同的 OS 线程中执行阻塞代码,而不会阻塞运行事件循环的 OS 线程。 "我认为默认情况下只有一个带有事件循环的线程,使用您自己的线程池可以避免带有事件循环的主线程阻塞
-
你想的没错,确实只有一个线程在运行事件循环。这个线程池是为你这种情况提供的一个帮手的东西,避免每个人都创建自己的线程池和线程数无限制的增长。所以你必须使用 a 线程池是真的,但它不一定是你自己的 - asyncio 提供的 thread pool (与线程不同)运行事件循环)非常好。
标签: python async-await python-asyncio