并行循环中正确使用std::atomic<double>,C++17/20有无内置实现?
问题分析与解决方案
原代码的线程安全问题
原代码中,对std::atomic<double>的if (s > max_value) { max_value = s; }并非原子操作。多线程环境下会出现竞态:比如线程A读取max_value为0,判断s=1符合更新条件,还未完成赋值时,线程B已将max_value更新为2;随后线程A把max_value设为1,直接覆盖了正确的最大值,导致结果错误。
正确实现方式
方式1:原子CAS操作修复
使用std::atomic的compare_exchange_weak(或compare_exchange_strong)实现原子性的条件更新。该操作会原子性地比较当前值与预期值,若相等则更新为新值,否则刷新预期值并重试:
#include <iostream> #include <atomic> #include <vector> #include <algorithm> #include <execution> int main() { constexpr int vec_size = 2000000; std::vector<double> numbers(vec_size, 1.0); numbers[vec_size / 2] = 2.0; std::atomic<double> max_value{ 0.0 }; std::for_each(std::execution::par_unseq, numbers.begin(), numbers.end(), [&max_value](double s) { double current_max = max_value.load(); while (s > current_max && !max_value.compare_exchange_weak(current_max, s)) { // 预期值已过时,循环重试 } }); std::cout << "Max Value: " << std::fixed << max_value; return 0; }
方式2:使用C++17并行算法(更推荐)
C++17引入了带执行策略的标准算法,其中std::reduce或std::max_element的并行版本会自动处理线程安全,无需手动管理原子操作:
用std::reduce计算最大值
#include <iostream> #include <vector> #include <algorithm> #include <execution> #include <numeric> #include <limits> int main() { constexpr int vec_size = 2000000; std::vector<double> numbers(vec_size, 1.0); numbers[vec_size / 2] = 2.0; // 用并行reduce计算最大值,初始值设为double的最小值 double max_value = std::reduce(std::execution::par_unseq, numbers.begin(), numbers.end(), std::numeric_limits<double>::lowest(), [](double a, double b) { return std::max(a, b); }); std::cout << "Max Value: " << std::fixed << max_value; return 0; }
用并行版std::max_element
#include <iostream> #include <vector> #include <algorithm> #include <execution> int main() { constexpr int vec_size = 2000000; std::vector<double> numbers(vec_size, 1.0); numbers[vec_size / 2] = 2.0; auto max_it = std::max_element(std::execution::par_unseq, numbers.begin(), numbers.end()); std::cout << "Max Value: " << std::fixed << *max_it; return 0; }
C17/C20的内置支持
C++17已经内置了完整的并行算法支持(需编译器支持并链接对应并行库,比如GCC的-ltbb):
std::max_element:支持std::execution::par/std::execution::par_unseq执行策略,内部会安全完成并行最大值计算;std::reduce:通用并行归约算法,可用于计算最大值、求和等,相比手动原子操作更高效(通常先做局部归约再合并,减少原子操作开销)。
C20未新增并行算法核心功能,但优化了执行策略的语义与实现细节,上述方案在C20中同样适用且更成熟。
内容的提问来源于stack exchange,提问作者Dieter Blocher
相关产品推荐
相关产品推荐

