关于返回std::move(unique_lock<>)的C++并发编程疑问
关于返回
std::move(std::unique_lock)的并发代码疑问解答 1. 这段代码会触发两次加锁和解锁吗?
不会。整个流程的锁状态变化是:
wait_for_data()中构造head_lock时,对head_mutex加锁;- 调用
data_cond.wait()时,unique_lock会临时解锁,等待条件满足后重新加锁; - 返回
std::move(head_lock)是转移锁的所有权,不会触发解锁——unique_lock的移动操作仅转移所有权,不改变锁的持有状态; do()中接收移动过来的unique_lock,只是接管锁的所有权,不会再次加锁;- 最终只有
do()中的head_lock析构时,才会解锁一次。
全程只有一次完整的加解锁周期,不存在两次加解锁。
2. 与单次加解锁示例的异同
相同点
- 核心逻辑一致:都是对
head_mutex完成一次加锁→条件等待→共享数据操作→解锁的流程; - 线程安全保障相同:都确保在持有锁的状态下执行
pop_head(),避免竞态条件; - 条件变量的行为一致:都通过
data_cond.wait()等待head!=get_tail()的条件,期间会自动处理锁的临时解锁与重加锁。
不同点
- 代码结构:前者将等待逻辑封装为独立的
wait_for_data()函数,实现职责分离;后者将所有逻辑写在同一函数内,结构更紧凑; - 锁的传递方式:前者通过移动语义转移
unique_lock的所有权,把持有锁的状态从等待函数传递到业务函数;后者全程由同一个unique_lock持有锁; - 复用性:前者的
wait_for_data()可被其他需要等待该条件的函数复用,后者的等待逻辑无法直接复用。
3. 返回std::unique_lock<>的意义
- 维持锁的持有状态:条件满足后,
unique_lock处于持有锁的状态,返回它可以将这个状态传递给调用者,让调用者能直接在持有锁的前提下执行后续共享数据操作(如pop_head()),避免了解锁后重新加锁可能引发的竞态问题; - 职责分离与代码复用:把等待条件的逻辑封装成独立函数,让业务函数专注于数据操作,同时该等待函数可被多个调用方复用,减少重复代码;
- 灵活的锁管理:
std::unique_lock支持移动语义,允许所有权转移,这是std::lock_guard无法做到的——如果用lock_guard,函数返回时会自动解锁,无法将持有锁的状态传递给调用者。
以下是原代码的格式化版本:
示例1:返回std::move(unique_lock)的代码
std::unique_lock<std::mutex> wait_for_data() { std::unique_lock<std::mutex> head_lock(head_mutex); data_cond.wait(head_lock,[&]{return head!=get_tail();}); return std::move(head_lock); } std::unique_ptr<node> do() { std::unique_lock<std::mutex> head_lock(wait_for_data()); return pop_head(); }
示例2:单次加解锁的代码
std::unique_ptr<node> do() { std::unique_lock<std::mutex> head_lock(head_mutex); data_cond.wait(head_lock,[&]{return head!=get_tail();}); return pop_head(); }
内容的提问来源于stack exchange,提问作者ppka
相关产品推荐
相关产品推荐

