当前位置: 首页 > news >正文

条款16:保证const成员函数的线程安全性

1 举个栗子

  1. 设计一个多项式类,roots函数被声明为const以确保不会修改多项式:
class Polynomial {
public:using RootsType = std::vector<double>;  	// 用于存储根的数据结构		… 						RootsType roots() const; 	// 用于计算并返回多项式的根(即使得多项式为零的解)};

为了避免不必要的计算和重复计算,可以采用缓存机制
逻辑上,roots函数不会改变它操作的多项式对象,但是在缓存过程中,它可能需要修改rootVals和rootsAreValid。
mutable关键字用于允许在常量函数(如roots)中修改类的某些成员变量。

class Polynomial {
public:using RootsType = std::vector<double>;RootsType roots() const{if (!rootsAreValid) { // 检查缓存是否有效// 如果无效,执行计算根的操作,并将结果存储在rootVals中rootsAreValid = true;}return rootVals;}
private:mutable bool rootsAreValid{ false };  mutable RootsType rootVals{};   
};

由于mutable成员的存在,虽然roots被声明为const函数,但可能在没有同步的情况下读写相同的内存,引发数据竞争,导致未定义的行为。

Polynomial p;/*----- 线程 1 ----- */     /*------- 线程 2 ------- */
auto rootsOfP = p.roots();  auto valsGivingZero = p.roots();

解决这个问题最简单的方法是:使用互斥锁。
由于std::mutex是一种仅可移动类型,将m添加到Polynomial,将导致Polynomial失去了被复制的能力。

class Polynomial {
public:using RootsType = std::vector<double>;RootsType roots() const{std::lock_guard<std::mutex> g(m); 	// 锁定mutexif (!rootsAreValid) { 		// 如果缓存无效// 计算/存储 rootValsrootsAreValid = true;}return rootVals;} // 解锁mutex
private:
//std::mutex m被声明为mutable,否则在roots函数内部,将被视为const对象,无法锁定和解锁mutable std::mutex m;mutable bool rootsAreValid{ false };mutable RootsType rootVals{};
};
  1. 在某些情况下,可能没必要使用互斥锁。
    例如,如果只是计算成员函数被调用的次数,使用std::atomic 变量通常是一种成本较低的方法。
    与std::mutex一样,std::atomic也是仅可移动类型,因此Point中存在callCount意味着Point也是仅可移动的。
class Point { // 2D 点
public:...double distanceFromOrigin() const noexcept {++callCount; // 原子递增return std::sqrt((x * x) + (y * y));}
private:mutable std::atomic<unsigned> callCount{ 0 };double x, y;
};

由于对std::atomic变量的操作通常比互斥锁开销更低,可能会导致过度依赖std::atomic:

class Widget {
public:...int magicValue() const{if (cacheValid) return cachedValue;else {auto val1 = expensiveComputation1();auto val2 = expensiveComputation2();cachedValue = val1 + val2;  // 糟糕cacheValid = true;          // 糟糕return cachedValue;}}
private:mutable std::atomic<bool> cacheValid{ false };mutable std::atomic<int> cachedValue;
};

这种方式有效,但请考虑以下情况:
1)一个线程调用Widget::magicValue,发现cacheValid为假,执行两个昂贵的计算。
2)此时,第二个线程调用Widget::magicValue,也看到cacheValid为假,因此执行了与第一个线程刚刚完成的相同的昂贵计算。(这个“第二个线程”实际上可能是其他几个线程。)
这种行为与缓存的目标背道而驰。

交换对cachedValue和cacheValid的赋值顺序可以消除这个问题,但结果更糟:

class Widget {
public:int magicValue() const{if (cacheValid) return cachedValue;else {auto val1 = expensiveComputation1();auto val2 = expensiveComputation2();cacheValid = true; 			// 糟糕return cachedValue = val1 + val2; 	// 糟糕}}};

想象一下,cacheValid为假,然后:
1)一个线程调用Widget::magicValue并将cacheValid设置为true。
2)就在那一刻,第二个线程调用Widget::magicValue并检查cacheValid。看到它为true,线程返回cachedValue,即使第一个线程尚未对其进行赋值。因此,返回的值是不正确的。

  1. 对单个变量或内存位置,使用std::atomic;涉及到更多需要作为一个整体操作的变量或内存位置,使用互斥锁:
class Widget {
public:int magicValue() const{std::lock_guard<std::mutex> guard(m); // 锁定mif (cacheValid) return cachedValue;else {auto val1 = expensiveComputation1();auto val2 = expensiveComputation2();cachedValue = val1 + val2;cacheValid = true;return cachedValue;}} // 解锁mprivate:mutable std::mutex m;mutable int cachedValue; // no longer atomicmutable bool cacheValid{ false }; // no longer atomic
};

2 要点速记

  1. 除非确定它们永远不会在并发环境中使用,否则需要确保const成员函数的线程安全。
  2. 使用 std::atomic变量可能比使用互斥锁提供更好的性能,但是它只适合操作单个变量或内存位置。
http://www.dtcms.com/a/499413.html

相关文章:

  • 网站开发需求现在网站怎么备案
  • 巧用LEF实现row aware track规划
  • 大话数据结构之 <栈> 和<队列>(C语言)
  • Windows 系统的 Delivery Optimization后台用了几GB流量,如何暂停?
  • 基于ads1256的ADC控制实现
  • 建站之星破解版手机正规建网站企业
  • 建一个电商网站要多少钱wordpress及时聊天
  • 云端思维导图软件,多设备同步无压力
  • Python Web 开发:从框架到实战案例
  • 做网站每天任务及实训过程公司关于网站建设的通知
  • 网站联系方式修改织梦网站建设是在商标哪个类别
  • 网站管理员密码在哪里找个人做网站的
  • C# 中,依赖注入(DI)的实现方式
  • java微服务驱动的社区平台:友猫社区的功能模块与实现逻辑
  • Flask入门教程——李辉 第三章 关键知识梳理
  • 产品更新与重构策略:创新与稳定的平衡之道
  • 【微服务】(1) Spring Cloud 概述
  • 做外贸球衣用什么网站嘉兴做微网站
  • 京华建设科技有限公司网站中华建筑网校
  • 合肥市高新区2025年初中信息学竞赛试题T1-T4 C++ 有故事听[doge]
  • Day 13 root 相关说明--以 ANAEX01 为实例
  • [Linux]学习笔记系列 -- [kernel][lock]debug_locks
  • Linux中双向链表介绍
  • 建设网站的运行费包括什么地方企业做网站哪家公司好
  • 产品频繁重构:企业发展的双刃剑
  • 微软Win11双AI功能来袭:“AI管家”+聊天机器人重构桌面交互体验
  • 2025年SEVC SCI2区,改进混沌多元宇宙算法+可重构作业车间物料配送优化,深度解析+性能实测,深度解析+性能实测
  • 建设网站的网站底压电工证wordpress导航主题模板下载地址
  • 自己做的网站怎么弄成app包装产品做网站
  • [GO]GORM中的Tag映射规则