C错误的分享示例
False sharing发生在多个线程修改同一缓存行中的不同变量时,导致缓存失效和性能下降;1. 使用结构体填充使每个变量独占一个缓存行;2. 利用alignas或std::hardware_destructive_interference_size进行内存对齐;3. 优先使用线程本地变量最后合并结果,从而避免伪共享,提升多线程程序性能。
在多线程编程中,false sharing(伪共享)是一个常见的性能陷阱。它发生在多个线程修改位于同一缓存行(cache line)中的不同变量时,导致不必要的缓存失效和性能下降。C 中尤其容易出现这个问题,因为开发者对内存布局有直接控制。

下面是一个典型的 C false sharing 示例,以及如何修复它。
? 什么是 False Sharing?
现代 CPU 缓存以“缓存行”为单位(通常是 64 字节)加载数据。如果两个变量位于同一个缓存行中,即使它们被不同的线程独立访问,一个线程的写操作也会导致另一个线程的缓存行失效,从而强制重新从内存加载 —— 这就是伪共享。

? 伪共享示例代码
#include <thread> #include <vector> #include <chrono> #include <iostream> alignas(64) struct Counter { volatile long value = 0; }; const int NUM_THREADS = 4; const long ITERATIONS = 10'000'000; // 多个线程共享一个数组,但每个线程操作不同元素 std::vector<Counter> counters_bad(NUM_THREADS); void worker_bad(int tid) { for (long i = 0; i < ITERATIONS; i) { counters_bad[tid].value ; } } int main() { std::vector<std::thread> threads; auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < NUM_THREADS; i) { threads.emplace_back(worker_bad, i); } for (auto& t : threads) { t.join(); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); std::cout << "Bad (false sharing): " << duration.count() << " ms\n"; return 0; }
⚠️ 虽然每个线程操作的是
counters_bad[tid]
,但Counter
结构体只用了alignas(64)
对单个变量对齐,而std::vector
中的元素是连续存储的。如果Counter
没有足够填充,多个Counter
可能挤在同一个缓存行中,造成伪共享。
但上面代码其实还不够典型,我们来写一个更清晰的对比。

? 对比:有伪共享 vs 无伪共享
#include <thread> #include <vector> #include <chrono> #include <iostream> // 方式1:容易产生 false sharing(变量太近) struct CounterFalse { volatile long a = 0; // 没有填充,多个 CounterFalse 可能共用一个缓存行 }; // 方式2:避免 false sharing,手动填充到一个缓存行 struct CounterFixed { volatile long a = 0; char padding[64 - sizeof(long)]; // 填充到 64 字节 }; const int NUM_THREADS = 4; const long ITERATIONS = 10'000'000; // 测试1:False sharing 版本 std::vector<CounterFalse> counters_false(NUM_THREADS); void worker_false(int tid) { for (long i = 0; i < ITERATIONS; i) { counters_false[tid].a ; } } // 测试2:无 false sharing 版本 std::vector<CounterFixed> counters_fixed(NUM_THREADS); void worker_fixed(int tid) { for (long i = 0; i < ITERATIONS; i) { counters_fixed[tid].a ; } } int main() { std::vector<std::thread> threads; // 测试 false sharing auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < NUM_THREADS; i) { threads.emplace_back(worker_false, i); } for (auto& t : threads) t.join(); auto end = std::chrono::high_resolution_clock::now(); auto duration1 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); std::cout << "With false sharing: " << duration1.count() << " ms\n"; threads.clear(); // 测试 fixed version start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < NUM_THREADS; i) { threads.emplace_back(worker_fixed, i); } for (auto& t : threads) t.join(); end = std::chrono::high_resolution_clock::now(); auto duration2 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); std::cout << "Without false sharing: " << duration2.count() << " ms\n"; return 0; }
? 输出示例(在多核机器上)
With false sharing: 850 ms Without false sharing: 220 ms
可以看到,避免伪共享后性能提升显著(4倍左右)。
? 如何避免 False Sharing?
- ✅ 使用填充(padding):确保每个线程访问的变量独占一个缓存行(通常是 64 字节)。
- ✅ 使用
alignas(64)
:对结构体或变量强制对齐。 - ✅ 每个线程使用本地变量,最后合并:减少共享变量访问。
- ✅ 使用标准库提供的对齐工具(如 C 11 的
alignas
和std::hardware_destructive_interference_size
)
? C 17 引入了两个推荐常量:
std::hardware_destructive_interference_size
:避免伪共享的最小间隔(通常是缓存行大小)std::hardware_constructive_interference_size
:建议共享的大小
示例(C 17 起):
struct alignas(std::hardware_destructive_interference_size) Counter { volatile long value = 0; };
? 总结
- False sharing 看似无害,实则严重拖慢多线程程序。
- 当多个线程频繁写入“逻辑上独立但物理上靠近”的变量时,容易触发。
- 解决方法:内存填充 对齐,确保变量不在同一缓存行。
- 在性能敏感的并行程序中(如高频计数、并发统计),必须注意此问题。
基本上就这些,不复杂但容易忽略。
以上是C错误的分享示例的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undress AI Tool
免费脱衣服图片

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

目录什么是Succinct(PROVE)哪些风险投资支持Succinct(PROVE)?Succinct(PROVE)的工作原理SP1zkVM和Prover网络OPSuccinct技术跨链验证PROVE代币经济学代币详情2025、2026、2027-2030年Succinct(PROVE)价格预测Succinct(PROVE)价格预测Succinct(PROVE)价格预测:交易量扩张和上市势头2025年至20

打开软件或游戏时,突然出现“应用程序无法正常启动(0xc0000906)”的提示,许多用户都会感到困惑,不知从何下手。实际上,这类错误大多源于系统文件损坏或运行库缺失。别急着重装系统,本文为你提供几种简单有效的解决方法,助你快速恢复程序运行。一、0xc0000906错误到底是什么?错误代码0xc0000906属于Windows系统常见的启动异常,通常表示程序在运行时无法加载必要的系统组件或运行环境。该问题常出现在运行大型软件或游戏时,主要原因可能包括:必要的运行库未安装或遭到破坏。软件安装包不完

要使用C 中的正则表达式,需包含头文件,并利用其提供的函数进行模式匹配和文本处理。1.使用std::regex_match进行全字符串匹配,仅当整个字符串符合模式时返回true;2.使用std::regex_search在字符串中查找任意位置的匹配;3.使用std::smatch提取捕获组,通过matches[0]获取完整匹配,matches[1]及后续获取子匹配;4.使用std::regex_replace替换匹配的文本,支持用$1、$2等引用捕获组;5.可在构造regex时添加icase(

电脑提示“计算机中丢失MSVCP71.dll”,通常是因为系统缺少关键运行组件,导致软件无法正常加载。本文将深入解析该文件的功能、报错根源,并提供三种高效解决方案,助你快速恢复程序运行。一、MSVCP71.dll是什么?MSVCP71.dll属于MicrosoftVisualC 2003的核心运行库文件,属于动态链接库(DLL)类型,主要用于支持C 编写的程序调用标准函数、STL模板及基础数据处理模块。许多2000年代初开发的应用程序和经典游戏都依赖此文件运行。一旦该文件缺失或损坏,系

使用std::ifstream的seekg和tellg方法可跨平台获取文件大小,通过打开二进制文件并定位到末尾,利用tellg()返回字节数;2.C 17及以上推荐使用std::filesystem::file_size,代码简洁且通过异常处理错误,需启用C 17标准;3.在POSIX系统上可使用stat()函数高效获取文件大小,适用于性能敏感场景。应根据编译器和平台选择合适方法,优先使用std::filesystem(若可用),否则使用ifstream保证兼容性,或在Unix系统上使用st

C 中的运算符重载允许为自定义类型赋予标准运算符新行为,1.通过成员函数重载 返回新对象;2.重载 =修改当前对象并返回引用;3.友元函数重载

AbasicMakeFileAutomatesc compilationByByDefindingruleswithtargets和commands.2.KeyComponentsIncludeVariablesLikeCXX,CXXFlags,cxxflags,target,srcs,srcs,srcs,srcs,srcs,objstosimplifyConfiguration.3.AptertNrules.3.aptertnrules(compiles)comptiles $ compiles $:%

std::variant是C 17引入的类型安全联合体,能安全地持有多个指定类型之一的值,通过std::get、std::holds_alternative、std::visit和std::get_if等方法实现安全访问与类型检查,结合std::monostate可模拟可选值,推荐使用std::visit进行类型分发并避免大型类型列表以提升可维护性,最终确保类型安全和异常安全。
