Python中的异步任务同步原语:asyncio.Lock、Semaphore、Event与Condition
字数 711 2025-11-16 02:06:26
Python中的异步任务同步原语:asyncio.Lock、Semaphore、Event与Condition
在异步编程中,当多个协程需要访问共享数据或协调执行顺序时,需要使用同步原语来避免竞态条件。asyncio提供了多种同步机制,它们的设计理念与线程同步原语相似,但专门针对异步环境优化。
1. 异步锁(asyncio.Lock)
- 作用:确保同一时间只有一个协程可以进入临界区
- 实现原理:内部维护一个等待队列,协程获取锁时若锁已被占用,则挂起并加入队列
import asyncio
class SharedCounter:
def __init__(self):
self.value = 0
self.lock = asyncio.Lock()
async def increment(self):
async with self.lock: # 自动获取和释放锁
temp = self.value
await asyncio.sleep(0.1) # 模拟IO操作
self.value = temp + 1
# 不使用锁会导致竞态条件:多个协程可能读取到相同的初始值
2. 信号量(asyncio.Semaphore)
- 作用:限制同时访问资源的协程数量
- 核心机制:维护一个计数器,acquire时减1,release时加1
async def limited_worker(semaphore, id):
async with semaphore: # 最多允许3个协程同时执行
print(f"Worker {id} started")
await asyncio.sleep(1)
print(f"Worker {id} finished")
async def main():
semaphore = asyncio.Semaphore(3) # 并发上限为3
tasks = [limited_worker(semaphore, i) for i in range(5)]
await asyncio.gather(*tasks)
3. 事件(asyncio.Event)
- 作用:允许协程等待某个事件发生
- 状态机制:初始为未设置,set()后唤醒所有等待者
async def waiter(event):
print("等待事件触发...")
await event.wait() # 阻塞直到event被设置
print("事件已触发,继续执行")
async def setter(event):
await asyncio.sleep(2)
event.set() # 唤醒所有等待的协程
async def main():
event = asyncio.Event()
await asyncio.gather(waiter(event), setter(event))
4. 条件变量(asyncio.Condition)
- 作用:基于某些条件协调多个协程的执行
- 高级特性:结合了Lock和Event的功能,支持条件等待和通知
class BoundedBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
self.condition = asyncio.Condition()
async def put(self, item):
async with self.condition:
# 等待缓冲区有空间
while len(self.buffer) >= self.capacity:
await self.condition.wait()
self.buffer.append(item)
self.condition.notify_all() # 通知消费者
async def get(self):
async with self.condition:
# 等待缓冲区有数据
while len(self.buffer) == 0:
await self.condition.wait()
item = self.buffer.pop(0)
self.condition.notify_all() # 通知生产者
return item
5. 原理解析与使用要点
- 非阻塞设计:所有异步同步原语在无法立即获取时都会挂起当前协程,让出事件循环
- 公平性保证:等待队列通常采用FIFO顺序,避免饥饿现象
- 超时机制:支持带超时的wait方法,防止永久阻塞
- 与线程原语的区别:asyncio原语不能在线程间使用,线程原语会阻塞整个事件循环
最佳实践建议:
- 优先使用async with语法自动管理资源获取和释放
- 锁的持有时间应尽可能短,避免影响并发性能
- 条件变量的等待应放在while循环中,防止虚假唤醒
- 注意避免死锁:确保获取和释放的顺序一致性