目录
[一、 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 能保证原子性 ------ 这是错误的!它的核心作用是:
- 禁止编译器优化 :避免编译器将
value_缓存到寄存器(多线程下,寄存器中的值和内存中的值可能不一致); - 强制内存直访 :确保每次读写
value_都直接操作内存,而非寄存器,保证线程间的内存可见性; - 原子性的真正保障:由 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 核心要点)
Atomic.h封装了 GCC 内置的原子操作指令,实现无锁的线程安全整数操作,是 Muduo 并发编程的基础;volatile保证内存可见性(禁止编译器优化),原子性由 CPU 指令级操作保证(而非volatile);AtomicInt32/AtomicInt64对外提供简洁的接口,内部通过模板复用代码,兼顾易用性和极致效率。