解析muduo源码之 atomic.h

目录

[一、 atomic.h](#一、 atomic.h)

[1. Atomic.h 整体定位](#1. Atomic.h 整体定位)

[2. 逐模块拆解代码](#2. 逐模块拆解代码)

[3. 核心对比:原子操作 vs 普通整数(线程安全问题)](#3. 核心对比:原子操作 vs 普通整数(线程安全问题))

[4. 关键设计细节补充](#4. 关键设计细节补充)

[总结(Atomic.h 核心要点)](#总结(Atomic.h 核心要点))


一、 atomic.h

先贴出完整代码,再逐部分解释:

cpp 复制代码
// 本源代码的使用受 BSD 风格许可证约束
// 该许可证可在 License 文件中查阅。
//
// 作者:陈硕 (chenshuo at chenshuo dot com)

#ifndef MUDUO_BASE_ATOMIC_H
#define MUDUO_BASE_ATOMIC_H

#include "muduo/base/noncopyable.h"  // 不可拷贝基类

#include <stdint.h>  // 固定宽度整数类型定义

namespace muduo
{

namespace detail
{
// 原子整数模板类,保证整数操作的原子性
template<typename T>
class AtomicIntegerT : noncopyable  // 继承不可拷贝类,禁止拷贝和赋值(默认)
{
 public:
  // 构造函数,初始化原子整数值为 0
  AtomicIntegerT()
    : value_(0)
  {
  }

  // 如果你需要拷贝和赋值功能,可取消以下注释
  //
  // AtomicIntegerT(const AtomicIntegerT& that)
  //   : value_(that.get())  // 拷贝构造时获取对方的当前值
  // {}
  //
  // AtomicIntegerT& operator=(const AtomicIntegerT& that)
  // {
  //   getAndSet(that.get());  // 赋值时原子性地设置为对方的值
  //   return *this;
  // }

  // 获取当前值(原子操作)
  T get()
  {
    // 在 gcc >= 4.7 版本中,可使用:__atomic_load_n(&value_, __ATOMIC_SEQ_CST)
    // __sync_val_compare_and_swap 实现原子加载:比较并交换,这里用 0 和 0 比较,等价于直接读取值
    return __sync_val_compare_and_swap(&value_, 0, 0);
  }

  // 原子性地增加指定值,并返回增加前的旧值
  T getAndAdd(T x)
  {
    // 在 gcc >= 4.7 版本中,可使用:__atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST)
    // __sync_fetch_and_add:原子性地将 x 加到 value_ 上,返回操作前的原始值
    return __sync_fetch_and_add(&value_, x);
  }

  // 原子性地增加指定值,并返回增加后的新值
  T addAndGet(T x)
  {
    return getAndAdd(x) + x;
  }

  // 原子性地自增 1,并返回自增后的新值
  T incrementAndGet()
  {
    return addAndGet(1);
  }

  // 原子性地自减 1,并返回自减后的新值
  T decrementAndGet()
  {
    return addAndGet(-1);
  }

  // 原子性地增加指定值,不返回任何结果
  void add(T x)
  {
    getAndAdd(x);
  }

  // 原子性地自增 1,不返回任何结果
  void increment()
  {
    incrementAndGet();
  }

  // 原子性地自减 1,不返回任何结果
  void decrement()
  {
    decrementAndGet();
  }

  // 原子性地设置为新值,并返回设置前的旧值
  T getAndSet(T newValue)
  {
    // 在 gcc >= 4.7 版本中,可使用:__atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST)
    // __sync_lock_test_and_set:原子性地将 value_ 设置为 newValue,返回操作前的旧值
    return __sync_lock_test_and_set(&value_, newValue);
  }

 private:
  // 核心存储值,volatile 关键字防止编译器优化,保证内存可见性
  volatile T value_;
};
}  // namespace detail

// 定义常用的原子整数类型别名
typedef detail::AtomicIntegerT<int32_t> AtomicInt32;  // 32位原子整数
typedef detail::AtomicIntegerT<int64_t> AtomicInt64;  // 64位原子整数

}  // namespace muduo

#endif  // MUDUO_BASE_ATOMIC_H
1. Atomic.h 整体定位

Atomic.h 实现了模板化的原子整数类AtomicIntegerT),封装了 GCC 内置的原子操作指令,提供线程安全的无锁整数操作(如自增、自减、赋值),是 Muduo 并发编程的核心基础组件 ------ 比如统计连接数、请求数、定时器计数、线程 ID 生成等场景,都依赖它避免多线程竞态条件。

2. 逐模块拆解代码

(1)基础框架:命名空间与类定义

cpp 复制代码
#include "muduo/base/noncopyable.h"  // 继承noncopyable,默认禁用拷贝
#include <stdint.h>                  // 跨平台整数类型(int32_t/int64_t)

namespace muduo
{
// 内部细节命名空间:对外隐藏模板实现,只暴露typedef
namespace detail
{
// 模板类:支持任意整数类型(实际只用int32_t/int64_t)
template<typename T>
class AtomicIntegerT : noncopyable  // 原子整数默认禁用拷贝(避免浅拷贝问题)
{
 public:
  // 构造函数:初始化值为0,无锁且线程安全
  AtomicIntegerT()
    : value_(0)
  {
  }

  // 注释掉的拷贝/赋值:如需拷贝,需手动启用(通过原子操作实现,保证线程安全)
  // AtomicIntegerT(const AtomicIntegerT& that) : value_(that.get()) {}
  // ...

核心解释

  • detail 命名空间:将模板类隐藏在内部,对外只暴露 AtomicInt32/AtomicInt64,简化外部使用(符合 "最小暴露原则");
  • 继承 noncopyable:默认禁用拷贝(原子整数的拷贝需谨慎,若需启用需通过原子操作实现,注释中给出了示例);
  • 模板设计:复用代码,同时支持 32 位 / 64 位整数,兼顾跨平台性。

(2)核心成员变量:volatile T value_

cpp 复制代码
private:
  volatile T value_;

关键:volatile 的作用

很多人误以为 volatile 能保证原子性 ------ 这是错误的!它的核心作用是:

  1. 禁止编译器优化 :避免编译器将 value_ 缓存到寄存器(多线程下,寄存器中的值和内存中的值可能不一致);
  2. 强制内存直访 :确保每次读写 value_ 都直接操作内存,而非寄存器,保证线程间的内存可见性;
  3. 原子性的真正保障:由 GCC 内置的原子操作函数(__sync_* 系列)实现,volatile 仅为辅助。

(3)核心方法:原子操作实现(GCC 内置指令)

Muduo 依赖 GCC 内置的 __sync_* 系列函数(GCC 4.1+ 支持),这些函数会生成 CPU 原子指令(如 x86 的 lock 前缀),无需加锁即可保证操作原子性,效率远高于 mutex 加锁。

方法 实现逻辑 功能说明
get() __sync_val_compare_and_swap(&value_, 0, 0) 原子读取当前值:对比 value_ 和 0,相等则设为 0(无变化),返回旧值 → 等价于原子读
getAndAdd(T x) __sync_fetch_and_add(&value_, x) 原子累加:先返回旧值,再将 value_ += x
addAndGet(T x) getAndAdd(x) + x 原子累加:先 value_ += x,再返回新值
incrementAndGet() addAndGet(1) 原子自增(返回新值)
decrementAndGet() addAndGet(-1) 原子自减(返回新值)
getAndSet(T newValue) __sync_lock_test_and_set(&value_, newValue) 原子赋值:先返回旧值,再将 value_ 设为 newValue

get() 为例深度解释

为什么不用普通的 return value_

cpp 复制代码
// 错误示例:普通读取非原子操作
T bad_get() { return value_; } 

多线程下,普通读取可能读到 "中间值"(比如线程 A 正在执行 value_++,只完成了 "读取→加 1",还没 "写入",线程 B 就读取到了旧值);而 __sync_val_compare_and_swap 是 CPU 指令级的原子操作,保证读取过程不可中断。

(4)对外暴露的简化类型

cpp 复制代码
}  // namespace detail

// 对外只暴露两个具体类型,隐藏模板复杂度
typedef detail::AtomicIntegerT<int32_t> AtomicInt32;
typedef detail::AtomicIntegerT<int64_t> AtomicInt64;

}  // namespace muduo

核心解释 :外部代码只需使用 AtomicInt32/AtomicInt64,无需关心内部模板实现,兼顾易用性和封装性。

3. 核心对比:原子操作 vs 普通整数(线程安全问题)

(1)普通整数:多线程竞态(不安全)

cpp 复制代码
#include <thread>
#include <iostream>
// 普通int:多线程下自增会出现竞态
int count = 0;
void increment() {
  for (int i=0; i<100000; ++i) {
    count++;  // 非原子操作:读取→加1→写入,多线程下可能中断
  }
}

int main() {
  std::thread t1(increment);
  std::thread t2(increment);
  t1.join(); t2.join();
  std::cout << count << std::endl;  // 结果 < 200000(线程不安全)
}

(2)原子整数:无锁线程安全

cpp 复制代码
#include "muduo/base/Atomic.h"
#include <thread>
#include <iostream>
// 原子整数:无锁且线程安全
muduo::AtomicInt32 count(0);
void increment() {
  for (int i=0; i<100000; ++i) {
    count.incrementAndGet();  // 原子自增,不可中断
  }
}

int main() {
  std::thread t1(increment);
  std::thread t2(increment);
  t1.join(); t2.join();
  std::cout << count.get() << std::endl;  // 结果 = 200000(线程安全)
}
4. 关键设计细节补充
  • 无锁设计 :基于 CPU 原子指令(lock 前缀),比 mutex 加锁效率高 10~100 倍(无上下文切换开销);
  • 最小接口:只提供常用操作(get、add、inc、dec、set),避免冗余,降低使用成本;
  • 兼容性 :注释中提到 GCC 4.7+ 可改用 __atomic_* 系列函数(更标准),兼顾不同 GCC 版本;
  • 内存屏障__sync_* 系列函数隐含内存屏障(__ATOMIC_SEQ_CST),保证操作的顺序一致性。
总结(Atomic.h 核心要点)
  1. Atomic.h 封装了 GCC 内置的原子操作指令,实现无锁的线程安全整数操作,是 Muduo 并发编程的基础;
  2. volatile 保证内存可见性(禁止编译器优化),原子性由 CPU 指令级操作保证(而非 volatile);
  3. AtomicInt32/AtomicInt64 对外提供简洁的接口,内部通过模板复用代码,兼顾易用性和极致效率。
相关推荐
陳10302 小时前
C++:继承
开发语言·c++
数智工坊2 小时前
【操作系统-处理器调度】
java·linux·服务器·windows·ubuntu
济6172 小时前
Linux内核---vmlinux、zImage、uImage区别
linux·运维·服务器
xiaoye-duck2 小时前
C++ string 类使用超全攻略(下):修改、查找、获取及常见实用接口深度解析
开发语言·c++·stl
素雨迁喜2 小时前
Linux系列文章(3)指令和权限
linux·运维·服务器
程序员老舅2 小时前
【无标题】
c++·嵌入式·八股文·c++八股文·八股文面试题·c++面经·c++面试题
码界奇点2 小时前
基于DDD与CQRS的Java企业级应用框架设计与实现
java·开发语言·c++·毕业设计·源代码管理
天天向上的鹿茸2 小时前
用cursor连接ssh服务器开发项目
运维·服务器·ssh
Frank_refuel2 小时前
C++STL之set和map的接口使用介绍
数据库·c++·算法