在类成员函数中使用std::mutex与std::lock_guard的优选方式是什么?
循环中使用
std::lock_guard的两种实现对比 让我来帮你拆解这两种在循环里搭配互斥量使用std::lock_guard的实现方式,聊聊它们的差异和适用场景:
场景1:循环内部构造lock_guard
这种方式下,每次循环迭代都会创建一个std::lock_guard实例,迭代结束时随着作用域退出,自动解锁互斥量。
代码示例:
std::mutex s_mutex; class Foo { public: void bar() { for (/* 循环条件 */) { std::lock_guard<std::mutex> guard( s_mutex ); // 单次迭代的临界区数据操作 } // lock_guard超出作用域,自动释放mutex } };
特点与适用场景:
- 锁的持有时间极短,仅覆盖单次迭代的临界操作,其他线程有更多机会获取锁,并发性能更优。
- 适合循环内的操作完全独立、不需要跨迭代保持数据一致性的场景。
- 虽然每次迭代都会构造/析构
lock_guard,但它是轻量级对象,这个开销通常可以忽略不计。
场景2:循环外部构造lock_guard
这种方式会在循环启动前就获取锁,整个循环执行期间都持有锁,直到当前作用域结束(比如函数返回)才自动解锁。
代码示例:
std::mutex s_mutex; class Foo { public: void bar() { std::lock_guard<std::mutex> guard( s_mutex ); for (/* 循环条件 */) { // 整个循环周期内的临界区数据操作 } // lock_guard在函数结束时释放mutex } };
特点与适用场景:
- 只做一次锁的获取和释放,减少了锁操作的总开销,适合循环迭代次数极多、且锁操作开销相对不可忽略的场景。
- 能保证整个循环的操作是原子性的,其他线程无法在循环执行期间修改临界数据,适合迭代间操作有依赖、或者需要确保循环执行过程中数据一致性的场景。
- 缺点是锁的持有时间长,会导致其他线程长时间等待,并发性能较差——尤其是当循环执行耗时较长时。
如何选择?
- 如果循环内的每次操作都是独立的,不需要跨迭代保持数据一致性,优先选场景1,它能提供更好的并发能力。
- 如果整个循环需要作为一个完整的原子操作,或者迭代间操作有依赖,或者锁操作开销远大于循环内的业务操作,那么场景2更合适。
内容的提问来源于stack exchange,提问作者Francis Cugler
相关产品推荐
相关产品推荐

