为何std::lock支持死锁避免而std::try_lock不支持?锁顺序差异原因
我之前一直以为std::lock()和std::try_lock()的区别只在于:当锁不可用时,std::try_lock()会立即返回false,而std::lock()会进入阻塞状态。但查了Cppreference的文档后,发现两者的差异远不止这点——
std::lock 函数签名与说明
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
该函数使用死锁避免算法对给定的可锁对象
lock1、lock2……lockn进行加锁,以避免死锁。
std::try_lock 函数签名与说明
int try_lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn);
该函数会从第一个开始,按顺序调用
try_lock尝试对给定的可锁对象lock1、lock2……lockn加锁。
下面针对两个问题给出详细解答:
1. 为何std::lock()具备死锁避免能力,而std::try_lock()却没有?
核心原因在于两者的设计目标和实现逻辑完全不同:
std::lock的唯一使命就是安全拿到所有锁,绝不死锁。它背后采用的是类似「循环加锁+回退重试」的死锁避免策略——比如先按锁的内存地址排序,再按固定顺序加锁;或者尝试获取锁失败时,释放已持有的所有锁,然后重新开始尝试。这种「退回去重来」的逻辑从根源上打破了死锁的「循环等待」条件,自然不会出现死锁。- 而
std::try_lock的核心需求是非阻塞、快速返回结果。它需要明确告诉调用者「到底哪一把锁拿不到」(返回值是失败锁的索引,全部成功返回-1)。如果它也采用std::lock的回退重试逻辑,就无法做到「立即返回」——重试会消耗时间,违背了try_lock非阻塞的设计初衷。另外,按顺序尝试的逻辑能直接定位第一个失败的锁,这也是它返回值设计的必然要求。
2. 为何std::lock()中锁的顺序无关紧要,而std::try_lock()会严格保持锁的顺序?
这依然由两者的优先级目标决定:
- 对于
std::lock,死锁避免是头等大事。它内部会自动对传入的锁进行统一排序(比如按锁对象的内存地址从小到大),不管你传入的顺序是lock2, lock3, lock1还是其他,最终都会按排序后的顺序加锁。这样所有调用std::lock的线程都会遵循相同的锁顺序,彻底打破死锁的「循环等待」条件,所以传入顺序根本不重要。 - 对于
std::try_lock,可预测的失败位置和非阻塞是核心。它必须严格按照你传入的顺序依次尝试:先拿lock1,成功了再拿lock2,一旦某把锁拿不到,立刻返回该锁的索引,同时释放已拿到的所有锁。这种顺序性让调用者能明确知道问题出在哪一把锁上,方便后续处理;如果打乱顺序,返回值的意义就不存在了,也违背了调用者对「尝试顺序」的预期。
内容的提问来源于stack exchange,提问作者Alok
相关产品推荐
相关产品推荐

