如何在非异步函数中调用异步协程task2并避免死锁?
如何在非异步函数中调用异步协程task2并避免死锁?
嗨,我看了你的代码,问题出在事件循环被阻塞导致协程无法调度上!咱们一步步拆解清楚:
你的代码里,task1启动线程后直接调用t.join(),这会把task1的协程彻底卡住,连带着主线程的事件循环也被阻塞了——而task2的协程需要事件循环来调度执行,结果就是线程里的future.result()一直在等task2完成,可task2根本没机会被事件循环跑起来,死锁就这么发生了。
下面给你两个可行的解决思路,都是亲测有效的:
方案一:给非异步函数单独新建事件循环
既然不想和主线程的事件循环抢资源,那干脆在func里新建一个独立的事件循环来运行task2,两个循环互不干扰,自然不会有死锁问题。修改后的func代码如下:
def func(): print("running func...") # 新建一个完全独立的事件循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # 用新循环把task2跑完拿到结果 result = loop.run_until_complete(task2()) loop.close() # 用完记得关闭循环,避免资源浪费 print(f"{result=}") print("done func...")
对应的task1里直接调用func()就行,不用手动创建线程、传循环这些麻烦事:
async def task1(): print("starting task1...") await asyncio.sleep(1) # 直接调用同步函数func,内部会自己处理异步逻辑 func() print("finished task1.")
方案二:不要阻塞主线程的事件循环
如果你想复用主线程的事件循环,核心就是别让task1的协程把事件循环卡死。原来的t.join()是硬阻塞,咱们换成异步等待线程完成的方式,用asyncio.to_thread把func放到线程池里跑,这样事件循环能继续调度task2的协程。
修改后的task1代码:
async def task1(): print("starting task1...") await asyncio.sleep(1) # 用to_thread异步等待func完成,不会阻塞事件循环 await asyncio.to_thread(func) print("finished task1.")
func可以保留你原来用run_coroutine_threadsafe的写法,甚至不用手动传循环:
def func(loop=None): print("running func...") if not loop: loop = asyncio.get_running_loop() future = asyncio.run_coroutine_threadsafe(task2(), loop) result = future.result() print(f"{result=}") print("done func...")
这样一来,task1用await asyncio.to_thread(func)时,事件循环不会被卡住,能同时处理task2的协程,线程里的future.result()就能顺利拿到结果了。
再补一句为啥原来的写法会死锁
怕你记混,再帮你捋一遍关键逻辑:
- 你用
run_coroutine_threadsafe把task2提交到了主线程的事件循环 - 但
task1里的t.join()直接阻塞了主线程的事件循环,导致task2根本没机会被调度执行 - 线程里的
future.result()一直在等task2的结果,陷入无限等待,死锁就这么来了
内容来源于stack exchange
相关产品推荐
相关产品推荐

