C++锁粒度控制:数据复制成本过高时,分段锁设计是否适得其反?
锁粒度控制方案的性能分析(基于《CPP-Concurrency-In-Action-2ed-2019》3.2.8章)
结论:当data_to_process的复制成本远高于process()的开销时,第一种手动控制锁粒度的设计确实会起到反效果。
我们可以拆解两种方案的核心开销构成:
- 第一种方案(手动锁控制+数据复制):锁的获取/释放开销 + 数据复制开销 +
process()处理开销 - 第二种方案(全程加锁+直接用引用):锁的持有开销(覆盖数据获取到
process()完成的全流程) +process()处理开销
当复制成本远大于process()的执行耗时,第一种方案的额外复制开销会完全抵消甚至超过“缩短锁持有时长”带来的并发收益:
- 从单线程执行效率看,原本全程加锁时线程只需在锁保护下完成数据获取和
process(),现在要额外花费大量时间在数据复制上——哪怕复制时没有持有锁,复制本身的耗时是新增的无收益开销,会拉长单线程的总执行时间。 - 从并发收益看,缩短锁持有时长的价值是减少其他线程的等待时间,但如果复制耗时比
process()还长,相当于把锁持有的时间转移成了复制的时间,整体的线程占用时间并没有减少甚至更长,其他线程的实际等待窗口并没有缩小多少,反而因为复制的额外耗时拖慢了整体吞吐量。
举个具体场景:如果data_to_process是包含百万级元素的std::vector,复制需要大量内存分配和逐元素拷贝,而process()只是简单统计元素个数——这种情况下,复制的耗时会是process()的几十甚至上百倍,第一种方案的总耗时会远高于第二种,完全得不偿失。
当然也可以考虑折中优化:如果get_next_data_chunk()返回的对象支持移动语义,用std::move()代替复制可以大幅降低数据转移的成本,这时候第一种方案的优势就能重新体现。但如果对象不可移动、或者移动成本依然很高,那么选择第二种全程加锁的方案会更合理。
内容的提问来源于stack exchange,提问作者f1msch
相关产品推荐
相关产品推荐

