题目重点:用数据库连接池和共享 map 两个场景解释二者不同,处理超时、异常释放与死锁。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 讨论常见同步原语:Mutex 用所有权规则排他保护一段共享状态;Counting Semaphore 有整数许可 P,最多 P 个进入。具体库可能对释放者/公平性规则不同。
解题路线
- 保护共享 map 不变量时用 mutex:进入临界区前 lock,读改写完成 unlock,同一时刻至多一持锁者。锁的所有权帮助防止无关线程释放,但具体行为以库为准。
- 限制最多 10 个数据库连接同时使用时用 semaphore(10):acquire 消耗一个许可,release 返还;它限制容量,不自动保护连接池内部 map/list 的一致性,因此可能还需 mutex。
- 二元 semaphore 初值 1 在容量上类似 mutex,但通常没有同样的所有权语义,不能直接说完全等价。
- 两者都要用 finally/RAII 保证异常路径释放;等待超时、取消和公平性决定饥饿风险。多个锁/许可嵌套须固定顺序,避免死锁。
手动走一遍
三个 worker 访问最多两条可用连接,semaphore(2) 允许两个同时持许可,第三个等待;若两个 worker 同时修改连接池队列,仍需 mutex 保证队列操作原子。
规模与取舍
acquire/release 原语本身通常近常数开销,但等待延迟取决于竞争和临界区时长;本题重点是并发不变量与许可数,不存在单一算法 Big-O。
易错点与追问
容易失分的边界
- 把 semaphore(10) 当成保护所有共享状态的锁。
- 认为 binary semaphore 与 mutex 所有语义相同。
- 异常路径漏 release,许可永久减少。
- 把 mutex 当可重入锁,未确认实现。
- 不考虑等待取消和饥饿。
继续深挖
- 用 semaphore 控制并发请求数,还需哪些锁?
- 二元 semaphore 和 mutex 何时不可替换?
- 如何防死锁与许可泄漏?
- 公平 semaphore 对吞吐/尾延迟有什么影响?
