0

0

c++怎么避免多线程中的伪共享(false sharing)_c++多线程伪共享问题分析与优化

穿越時空

穿越時空

发布时间:2025-11-03 21:38:02

|

988人浏览过

|

来源于php中文网

原创

伪共享是多线程下因变量同处一缓存行导致的性能问题,当多线程修改逻辑独立但物理相邻的变量时,引发频繁缓存同步,表现为吞吐量不升反降、缓存未命中率上升;可通过结构体填充、alignas对齐、数组间隔布局或线程本地存储等方法隔离写操作,结合硬件缓存行大小(如std::hardware_destructive_interference_size)进行优化,重点应用于高频写入场景以平衡性能与内存开销。

c++怎么避免多线程中的伪共享(false sharing)_c++多线程伪共享问题分析与优化

在C++多线程编程中,伪共享(False Sharing)是影响性能的一个常见但容易被忽视的问题。它发生在多个线程操作不同变量,而这些变量恰好位于同一个CPU缓存行中,导致缓存频繁失效,从而降低程序效率。

什么是伪共享?

现代CPU为了提高访问速度,会将内存按“缓存行”(Cache Line)为单位加载到高速缓存中,通常大小为64字节。当一个核心修改了某个缓存行中的变量,整个缓存行会被标记为无效,其他核心即使访问的是该行中不同的变量,也必须重新从内存加载。

例如:两个线程分别修改位于同一缓存行的变量 ab,虽然逻辑上无冲突,但由于共享缓存行,会导致反复的缓存同步,这就是伪共享。

如何识别伪共享问题?

性能表现特征:
  • 多线程吞吐量没有随核心数增加而提升,甚至下降
  • CPU缓存未命中率显著升高
  • 使用性能分析工具(如perf、Intel VTune)可发现大量缓存一致性流量

代码典型场景:

立即学习C++免费学习笔记(深入)”;

struct Counter {
    int64_t a; // 线程1写入
    int64_t b; // 线程2写入
};

若两个线程分别对 ab 进行频繁写操作,由于它们可能处于同一缓存行,就会产生伪共享。

避免伪共享的常用方法

核心思路是:确保被不同线程频繁写入的变量不在同一个缓存行中。

1. 手动填充(Padding)

通过添加填充字节,使每个变量独占一个缓存行。

struct PaddedCounter {
    int64_t value;
    char padding[64 - sizeof(int64_t)]; // 填充至64字节
};

若用于数组,每个元素都应独立占据缓存行。

2. 使用对齐属性(alignas

Magic Eraser
Magic Eraser

AI移除图片中不想要的物体

下载

C++11起支持 alignas,强制变量按缓存行对齐。

struct alignas(64) AlignedCounter {
    int64_t value;
};

这样即使结构体较小,也会占用完整缓存行,避免与其他数据共享。

3. 数组布局优化

对于计数器数组等场景,不要让每个线程写相邻元素。

// 每个线程使用间隔至少64字节的槽位
alignas(64) int64_t counters[NUM_THREADS][8]; // 每行64字节
// 线程i使用 counters[i][0]

或使用一维数组并手动跳过缓存行:

int64_t* counters = new int64_t[NUM_THREADS * 8];
// 线程i访问 counters[i * 8]

4. 使用线程本地存储(TLS)

每个线程先累加本地副本,最后合并结果,从根本上避免共享写入。

thread_local int64_t local_sum = 0;

// 工作循环中 local_sum += delta;

// 最终合并 global_counter.fetch_add(local_sum);

适用于累加、统计类场景,效果显著。

实际建议与注意事项

伪共享主要影响频繁写入的场景,读操作影响较小。优化时注意:

  • 不是所有共享变量都需要padding,只针对高频写入且跨线程的变量
  • 过度填充会浪费内存,需权衡空间与性能
  • 缓存行大小因平台而异(多数为64字节),可用 std::hardware_destructive_interference_size 获取(C++17起)
#ifdef __cpp_lib_hardware_interference_size
    constexpr size_t cacheline_size = std::hardware_destructive_interference_size;
#else
    constexpr size_t cacheline_size = 64;
#endif

基本上就这些。关键是在设计并发数据结构时,有意识地隔离线程间的写操作,合理利用对齐和本地存储,就能有效规避伪共享带来的性能陷阱。

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

197

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

189

2025.07.04

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

535

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

17

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

21

2026.01.06

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

481

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

143

2025.12.24

css中的padding属性作用
css中的padding属性作用

在CSS中,padding属性用于设置元素的内边距。想了解更多padding的相关内容,可以阅读本专题下面的文章。

133

2023.12.07

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

23

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 4.7万人学习

Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号