Asyncio Lock使用异常求助:多任务互斥执行报错
Asyncio Lock 跨事件循环错误解决
问题场景
需要用Asyncio Lock避免同一设备的异步请求并发执行,测试代码添加Lock后出现RuntimeError: Task got Future attached to a different loop错误。
未加Lock的测试代码(正常并发):
import asyncio async def method(wait:int): print(f"Method with waiting {wait}s starting") await asyncio.sleep(wait) print(f"Method with waiting {wait}s finished") async def main(): task1 = asyncio.create_task(method(2)) task2 = asyncio.create_task(method(2)) await task1 await task2 asyncio.run(main())
输出:
Method with waiting 2s starting Method with waiting 2s starting Method with waiting 2s finished Method with waiting 2s finished
添加Lock后的错误代码:
import asyncio locker = asyncio.Lock() # 模块级别初始化Lock async def method(wait:int): async with locker: print(f"Method with waiting {wait}s starting") await asyncio.sleep(wait) print(f"Method with waiting {wait}s finished") async def main(): task1 = asyncio.create_task(method(2)) task2 = asyncio.create_task(method(2)) await task1 await task2 asyncio.run(main())
错误输出:
Method with waiting 2s starting Method with waiting 2s finished Traceback (most recent call last): File "/Users/filip/Desktop/Python/07_Asyncio2.py", line 16, in <module> asyncio.run(main()) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py", line 44, in run return loop.run_until_complete(main) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete return future.result() File "/Users/filip/Desktop/Python/07_Asyncio2.py", line 14, in main await task2 File "/Users/filip/Desktop/Python/07_Asyncio2.py", line 5, in method async with locker: File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/locks.py", line 14, in __aenter__ await self.acquire() File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/locks.py", line 120, in acquire await fut RuntimeError: Task <Task pending name='Task-3' coro=<method() running at /Users/filip/Desktop/Python/07_Asyncio2.py:5>> got Future <Future pending> attached to a different loop
错误原因
asyncio.Lock()在模块级别初始化时,会自动绑定到当前存在的事件循环。但asyncio.run()函数的特性是:每次调用都会创建一个全新的独立事件循环,执行完毕后关闭该循环。这就导致模块级的Lock绑定的循环和asyncio.run()内部创建的循环不是同一个,任务尝试获取Lock时出现循环不匹配的错误。
解决方案
1. 测试代码修复:在事件循环内创建Lock
将Lock的创建移到main()函数内部,确保Lock和任务处于同一个事件循环中:
import asyncio async def method(wait:int, locker): async with locker: print(f"Method with waiting {wait}s starting") await asyncio.sleep(wait) print(f"Method with waiting {wait}s finished") async def main(): locker = asyncio.Lock() # 在事件循环内部创建Lock task1 = asyncio.create_task(method(2, locker)) task2 = asyncio.create_task(method(2, locker)) await task1 await task2 asyncio.run(main())
运行输出(串行执行,符合预期):
Method with waiting 2s starting Method with waiting 2s finished Method with waiting 2s starting Method with waiting 2s finished
2. 实际场景(设备类):在类实例中创建Lock
针对多设备异步通信的场景,为每个设备实例单独创建Lock,确保同一设备的请求串行执行,不同设备的请求可以并发:
import asyncio class Device: def __init__(self, device_id): self.device_id = device_id self.lock = asyncio.Lock() # 每个设备实例拥有自己的Lock async def send_request(self, wait_time): async with self.lock: print(f"Device {self.device_id}: Request starting (wait {wait_time}s)") await asyncio.sleep(wait_time) # 模拟设备响应耗时 print(f"Device {self.device_id}: Request finished") async def main(): device1 = Device("Device-A") device2 = Device("Device-B") # 同一设备的两个请求串行,不同设备的请求并发 tasks = [ asyncio.create_task(device1.send_request(2)), asyncio.create_task(device1.send_request(2)), asyncio.create_task(device2.send_request(1)), asyncio.create_task(device2.send_request(1)) ] await asyncio.gather(*tasks) asyncio.run(main())
运行输出:
Device Device-A: Request starting (wait 2s) Device Device-B: Request starting (wait 1s) Device Device-B: Request finished Device Device-B: Request starting (wait 1s) Device Device-A: Request finished Device Device-A: Request starting (wait 2s) Device Device-B: Request finished Device Device-A: Request finished
内容的提问来源于stack exchange,提问作者chochf
相关产品推荐
相关产品推荐

