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

为何C++程序中使用std::lock_guard后运行时间反而更短?

为什么带std::lock_guard的多线程代码比无锁版本更快?

现象回顾

你的测试中,带锁版本耗时5.7s且结果正确(2000000000),无锁版本耗时10.7s且结果错误(1010269798),这和“锁会增加额外开销”的直觉认知相反,核心原因是无锁版本的严重缓存行颠簸抵消了并行收益,甚至比串行执行更慢。

核心原因分析

1. 无锁版本的致命问题:缓存行颠簸

a++并非原子操作,实际拆解为三个步骤:

  • 从缓存/内存加载a的值到CPU寄存器
  • 寄存器中的值执行+1操作
  • 将新值写回缓存/内存

当两个线程同时操作同一个int a时,受CPU缓存一致性协议(如MESI)约束:

  • 线程1修改a后,线程2缓存中a所在的缓存行会被标记为无效
  • 线程2必须重新从主内存加载a的最新值,这个操作的延迟远高于CPU内部运算
  • 这种“修改-失效-重新加载”的循环会在两个线程间反复触发,导致绝大多数CPU时间浪费在缓存同步上,而非实际的自增计算

这种现象就是缓存行颠簸(Cache Line Thrashing),它的开销远超过锁的开销,最终导致无锁版本运行更慢,同时因数据竞争破坏了自增操作的原子性,出现计数丢失。

2. 带锁版本的串行执行反而更高效

std::lock_guard保证同一时间只有一个线程能执行add()内的循环:

  • 第一个线程完整执行1e9次自增,期间a始终在该线程的缓存中,不会触发缓存失效
  • 第一个线程执行完毕释放锁后,第二个线程才开始执行,同样全程复用自身缓存
  • 锁本身的开销和缓存行颠簸的巨大开销相比可以忽略,因此总耗时更短,同时保证了结果的正确性

正确的并行优化方式

如果想真正利用多线程提升效率,应避免多个线程竞争同一个变量,比如让每个线程操作独立的局部变量,最后再汇总结果:

#include <iostream>
#include <thread>
#include <ctime>

using namespace std;

class test {
public:
    void add(int& local_a) {
        for(int i = 0; i < 1e9; i++) {
            local_a++;
        }
    }
};

int main() {
    test t;
    int a1 = 0, a2 = 0;
    auto start = clock();
    std::thread t1(&test::add, &t, ref(a1));
    std::thread t2(&test::add, &t, ref(a2));

    t1.join();
    t2.join();

    auto end = clock();

    cout << "total = " << a1 + a2 << endl;
    cout << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;

    return 0;
}

这个版本中两个线程各自操作独立变量,不会触发缓存行颠簸,能真正发挥多线程的并行优势,耗时会比带锁版本更短。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 07:35:22