You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何两个std::unique_lock可同时持有同一std::mutex锁?

问题:两个std::unique_lock同时显示持有同一std::mutex,但mutex实际未锁定

应用出现阻塞问题:两个独立线程中的不同std::unique_lock实例,owns_lock()均返回true,这与std::mutex的排他性矛盾。通过GDB调试发现:两个锁的_M_owns属性均为true,但关联的std::mutex的__lock、__count、__owner属性均为0,仅__nusers为2。

调试细节

线程信息

(gdb) info thread
  Id   Target Id                                         Frame 
* 1    Thread 0x7ffff7e897c0 (LWP 66233) "tests" __futex_abstimed_wait_common64 (private=0, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x612000002f28) at ./nptl/futex-internal.c:57
  33   Thread 0x7fffe5ee0640 (LWP 66269) "tests" __futex_abstimed_wait_common64 (private=0, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x612000002f28) at ./nptl/futex-internal.c:57

共享资源与锁实例地址

线程1和33共享类成员vec、m、cv,各自拥有独立的std::unique_lock实例:

# 线程1的地址信息
(gdb) select 5
(gdb) p &m
$14 = (std::mutex *) 0x612000002ed8
(gdb) p &cv
$15 = (std::condition_variable *) 0x612000002f00
(gdb) p &vec
$16 = (std::__debug::vector<std::shared_future<void>, std::allocator<std::shared_future<void> > > *) 0x612000002fb0
(gdb) p &l
$17 = (std::unique_lock<std::mutex> *) 0x7fffffffd240

# 线程33的地址信息
(gdb) t 33
[Switching to thread 33 (Thread 0x7fffe5ee0640 (LWP 67477))]
#0  __futex_abstimed_wait_common64 (private=0, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x612000002f28) at ./nptl/futex-internal.c:57
57      in ./nptl/futex-internal.c
(gdb) select 5
(gdb) p &m
$18 = (std::mutex *) 0x612000002ed8
(gdb) p &cv
$19 = (std::condition_variable *) 0x612000002f00
(gdb) p &vec
$20 = (std::__debug::vector<std::shared_future<void>, std::allocator<std::shared_future<void> > > *) 0x612000002fb0
(gdb) p &l
$21 = (std::unique_lock<std::mutex> *) 0x7fffe5edf8c0

阻塞位置

  • 线程1阻塞在第94行:cv.wait(l);
  • 线程33阻塞在第54行:cv.wait(l);

锁与mutex状态

线程1的锁实例与mutex状态:

(gdb) t 1
[Switching to thread 1 (Thread 0x7ffff7e897c0 (LWP 67445))]
#0  __futex_abstimed_wait_common64 (private=0, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x612000002f28) at ./nptl/futex-internal.c:57
57      ./nptl/futex-internal.c: No such file or directory.
(gdb) select 5
(gdb) p l
$22 = {_M_device = 0x612000002ed8, _M_owns = true}
(gdb) p m
$23 = {<std::__mutex_base> = {_M_mutex = {__data = {__lock = 0, __count = 0, __owner = 0, __nusers = 2, __kind = 0, __spins = 0, __elision = 0, __list = {__prev = 0x0, __next = 0x0}}, 
      __size = '\000' <repeats 12 times>, "\002", '\000' <repeats 26 times>, __align = 0}}, <No data fields>}

线程33的锁实例与mutex状态:

(gdb) t 33
[Switching to thread 33 (Thread 0x7fffe5ee0640 (LWP 67477))]
#0  __futex_abstimed_wait_common64 (private=0, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x612000002f28) at ./nptl/futex-internal.c:57
57      in ./nptl/futex-internal.c
(gdb) select 5
(gdb) p l
$24 = {_M_device = 0x612000002ed8, _M_owns = true}
(gdb) p m
$25 = {<std::__mutex_base> = {_M_mutex = {__data = {__lock = 0, __count = 0, __owner = 0, __nusers = 2, __kind = 0, __spins = 0, __elision = 0, __list = {__prev = 0x0, __next = 0x0}}, 
      __size = '\000' <repeats 12 times>, "\002", '\000' <repeats 26 times>, __align = 0}}, <No data fields>}

疑问:为何两个std::unique_lock实例可同时显示持有锁(l.owns_lock() == true),而关联的std::mutex实际未被锁定?


解答

这是std::condition_variable::wait的正常行为,从调试信息看两个线程都处于wait调用中,核心逻辑如下:

  1. wait的执行流程:调用cv.wait(l)时,会自动完成两个关键操作:

    • 释放持有的std::mutex(对应mutex的__lock变为0、__owner清零)
    • 阻塞等待条件变量的通知

    此时std::unique_lock的_M_owns标记不会立即修改——这是逻辑层面的持有标记,并非mutex实际状态的实时映射。wait内部保留这个标记,是为了在被唤醒时自动重新获取mutex,确保唤醒后线程能恢复持有锁的状态。

  2. mutex的__nusers字段含义:__nusers=2仅表示当前有两个std::unique_lock实例关联到该mutex,和两个线程各自持有一个lock实例的情况完全吻合,不代表同时持有锁。

  3. 状态矛盾的解释:此时两个线程都处于等待唤醒的状态,mutex未被任何线程持有,但unique_lock的owns_lock()返回true是wait调用的约定——它保证调用wait前线程持有锁,唤醒后线程会重新持有锁,中间的释放和重新获取是wait内部的原子操作,unique_lock的标记维持true是为了封装这个过程,避免外部代码误解锁的状态。

总结:这种状态是condition_variable::wait执行过程中的中间状态,完全符合C++标准库的行为逻辑,并未违反mutex的排他性。当任意线程被唤醒时,会先重新获取mutex(此时mutex的__lock和__owner会更新为对应线程的信息),unique_lock的标记依然保持true,逻辑与实际状态再次一致。


内容的提问来源于stack exchange,提问作者norritt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 10:49:54