Python异步等待函数传入布尔变量后while循环条件无法更新的问题
嗨,我来帮你捋清楚这个问题~
你遇到的情况本质上是Python里不可变类型的参数传递特性导致的:像布尔值、整数、字符串这类不可变类型,当你把它们作为参数传给函数时,传递的是当前值的一份拷贝,而不是指向原变量的“指针”或者引用。
你看,当你调用wait_for(action_done)的时候,action_to_wait拿到的是action_done当时的False值,之后哪怕function_A把全局的action_done改成了True,wait_for里的action_to_wait还是原来的False拷贝,所以那个while循环会一直跑下去,永远不会终止。而wait_for_action_to_be_done()是直接读取全局的action_done,每次循环都会去拿最新的值,所以能正常工作。
那怎么解决这个问题呢?给你两种常用的方案:
方案一:用可变容器包裹布尔值
既然不可变类型传的是值拷贝,那我们可以用一个可变的容器(比如列表)来装这个布尔值,这样传递的是容器的引用,修改容器里的元素时,函数内部能看到变化:
import asyncio action_done = [False] # 用列表包裹布尔值 async def function_A(delay): global action_done await asyncio.sleep(delay) action_done[0] = True # 修改列表里的元素 async def wait_for(action_to_wait): print("wait for action to be done...") while not action_to_wait[0]: # 检查列表里的元素 await asyncio.sleep(0.1) async def function_B(): await wait_for(action_done) print("performing next action...") async def main(): await asyncio.gather(function_A(2), function_B()) asyncio.run(main())
方案二:用asyncio.Event(更推荐的异步同步方式)
其实在Python异步编程里,有专门的同步原语来处理这种“等待某个动作完成”的场景,那就是asyncio.Event,它比自己写while轮询更高效,也更符合异步编程的规范,还能避免全局变量带来的潜在问题:
import asyncio async def function_A(delay, event): await asyncio.sleep(delay) event.set() # 触发事件,标记动作完成 async def wait_for(event): print("wait for action to be done...") await event.wait() # 等待事件被触发,不需要自己写轮询循环 async def function_B(event): await wait_for(event) print("performing next action...") async def main(): action_event = asyncio.Event() # 创建事件对象 await asyncio.gather(function_A(2, action_event), function_B(action_event)) asyncio.run(main())
这个方案里,event.wait()会自动挂起协程,直到event.set()被调用,不需要每隔0.1秒轮询一次,效率更高,代码也更简洁。
总结一下:你的问题核心是不可变类型的参数传递机制,用可变容器或者asyncio.Event都能解决,后者是异步场景下更地道的做法哦~
备注:内容来源于stack exchange,提问作者Frankie

