因为需理解阻塞队列底层机制、定制唤醒逻辑或学习同步原语,而queue.Queue封装了Condition;threading.Condition中wait()必须在with块内调用,状态检查须用while防虚假唤醒,notify()不释放锁,且需正确配对唤醒目标。

为什么不用queue.Queue而要手写threading.Condition版本
因为想理解阻塞队列底层的等待/通知机制,或者需要定制唤醒逻辑(比如按优先级唤醒、带超时的条件判断),又或者在学习多线程同步原语。直接用queue.Queue当然更安全,但它把Condition封装掉了,你没法干预wait()前的检查逻辑或替换锁策略。
threading.Condition里wait()必须在with块里调用
这是最容易出RuntimeError: cannot wait on un-acquired lock的地方。Condition内部依赖关联的Lock,wait()会先释放锁再挂起线程,醒来后自动重新获取——但前提是调用前锁已被当前线程持有。
- 错误写法:
cond.wait()不在with cond:里,或锁被其他线程持有 - 正确结构:所有对共享状态的读写、以及
wait()/notify()都必须包裹在with self._cond:中 - 注意:
notify()不释放锁,所以多个notify()可以连续调用,但别在没加锁时调
实现put()和get()时的状态检查必须用while而不是if
因为notify()是“唤醒至少一个”,不保证被唤醒的线程立刻执行;也可能发生虚假唤醒(spurious wakeup)。如果只用if,线程醒来后直接取数据,可能发现队列还是空或满,导致IndexError或逻辑错乱。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
def get(self):
with self._cond:
while len(self._items) == 0:
self._cond.wait() # 等待非空
item = self._items.pop(0)
self._cond.notify() # 唤醒一个等待put的线程
return item-
put()同理:用while len(self._items) >= self._maxsize:判断是否满 - 哪怕设置了
maxsize=0(无界),也建议保留while,便于后续扩展 - 唤醒用
notify()就够了,除非你明确要唤醒所有等待者(比如广播关闭信号),才用notify_all()
初始化时传入的maxsize会影响put()阻塞行为
无界队列(maxsize=0)永远不会满,put()永不阻塞;有界队列则必须检查长度。这个值不改变Condition本身,但决定了while循环的退出条件。
立即学习“Python免费学习笔记(深入)”;
- 常见疏忽:忘记在
__init__里存self._maxsize = maxsize,导致put()里无法判断容量 - 性能提示:用
collections.deque代替list做_items,pop(0)和append()都是O(1) - 边界情况:
maxsize=1时,put()和get()极易互相阻塞,适合用来验证唤醒逻辑是否正常
真正难的是让notify()时机精准——比如put()后只该唤醒等get()的线程,get()后只该唤醒等put()的。混用或漏调都会导致死锁或饥饿。

















