解析muduo库对于互斥锁/读写锁/自旋锁/CAS/分布式锁的实现
摘要:muduo是陈硕开发的高性能C++网络库,采用Reactor多线程模型,广泛应用于游戏服务器开发。其同步原语设计特点包括:1)轻量封装Linux系统调用;2)强制RAII机制管理锁生命周期;3)仅支持单机级同步。主要锁类型包括:互斥锁(MutexLock)、读写锁(ReadWriteLock)和自旋锁(SpinLock),均提供对应的Guard类实现自动加解锁。原子操作基于C++11的std
核心前提
muduo是陈硕大佬实现的 Linux 下高性能 C++ 网络库,基于Reactor 多线程模型,是游戏服务器 / 后端服务器开发的主流选型;muduo 的所有同步原语(锁 / CAS)都实现于 muduo/base/ 目录下,核心设计思想只有 2 个:① 对 Linux 原生系统调用的轻量封装,无冗余逻辑、性能极致;② 强制 RAII 机制 管理锁的生命周期,杜绝「忘记解锁导致死锁」的低级 bug;③ 所有同步原语都是单机级,这是前提。
注意:
muduo是单机库,本身没有实现分布式锁,但是会探讨muduo库在游戏业务中的分布式锁方案。
前提背景
1. muduo 同步原语的核心共性
- 所有锁都提供
XXXLock(锁本体) +XXXLockGuard(RAII 封装) 成对的类,严禁直接调用 lock/unlock,必须用 Guard,出作用域自动解锁,这是 muduo 的强制规范; - 所有锁的底层都是 Linux 系统原生的同步接口,muduo 只做封装,不做自研,保证性能和稳定性;
- 所有锁都支持 非拷贝、非赋值(通过
delete拷贝构造和赋值运算符实现),保证锁的唯一性; - 所有锁的实现都在
muduo/base/Mutex.h、muduo/base/SpinLock.h、muduo/base/Atomic.h头文件中,源码极简,核心代码只有几十行。
2. muduo 的核心设计原则
性能优先,够用即可:muduo 的锁没有复杂的功能,只实现最核心的同步能力,所有性能损耗都来自底层系统调用,muduo 本身几乎无开销,这也是游戏服务器选择 muduo 的核心原因。
一、 muduo 互斥锁 (MutexLock / MutexLockGuard)
1. 底层实现原理
muduo 的互斥锁 = Linux pthread 库的pthread_mutex_t (POSIX 标准互斥锁) + C++ RAII 封装,是 muduo 中最基础、最常用的同步原语。
- Linux 的
pthread_mutex_t是内核态互斥锁,线程获取锁失败时,会让出 CPU、进入内核睡眠状态,直到锁被释放后被内核唤醒; - 锁的属性:非递归、非公平,同一线程重复加锁会直接死锁,锁竞争时线程唤醒顺序无保证。
2. muduo 互斥锁 核心源码实现
muduo 的互斥锁源码非常简洁,核心就 2 个类:MutexLock(锁本体) + MutexLockGuard(RAII 自动管理),以下是简化后的核心源码(和 muduo 源码一致):
// muduo/base/Mutex.h
#include <pthread.h>
#include <boost/noncopyable.hpp>
// 互斥锁本体,继承noncopyable,禁止拷贝赋值
class MutexLock : boost::noncopyable {
public:
MutexLock() {
pthread_mutex_init(&mutex_, nullptr); // 初始化pthread互斥锁
}
~MutexLock() {
pthread_mutex_destroy(&mutex_); // 销毁锁
}
// 加锁
void lock() {
pthread_mutex_lock(&mutex_);
}
// 解锁
void unlock() {
pthread_mutex_unlock(&mutex_);
}
// 获取原生锁句柄(供条件变量等使用)
pthread_mutex_t* getPthreadMutex() { return &mutex_; }
private:
pthread_mutex_t mutex_;
};
// RAII 锁守卫,核心!出作用域自动解锁,杜绝死锁
class MutexLockGuard : boost::noncopyable {
public:
// 构造函数:自动加锁
explicit MutexLockGuard(MutexLock& mutex) : mutex_(mutex) {
mutex_.lock();
}
// 析构函数:自动解锁
~MutexLockGuard() {
mutex_.unlock();
}
private:
MutexLock& mutex_;
};
// 宏定义:禁止手动调用lock/unlock,强制用Guard
#define MutexLockGuard(x) error "Missing guard object name"
muduo::MutexLock mutex;
void modifyPlayerBag(Player& player) {
muduo::MutexLockGuard lock(mutex); // RAII自动加锁
player.bag_.addItem(1001);
player.bag_.removeItem(2002);
} // 出作用域自动解锁
3. 特性 & 使用场景
- 特性:睡眠阻塞、独占式锁(同一时间只能一个线程持有)、性能稳定、无 CPU 空耗;缺点是有内核态 / 用户态上下文切换开销。
- 游戏场景:玩家背包修改、技能释放逻辑、公会数据修改、玩家属性批量更新等「临界区代码较长、竞争频率中等」的场景,游戏开发 90% 的单机并发场景都能用互斥锁兜底。
4. muduo 的递归互斥锁 MutexLockRecursive
muduo 还实现了递归互斥锁,底层是pthread_mutex_t的递归属性,允许同一线程重复加锁,解锁次数和加锁次数一致即可,适合「函数嵌套调用需要加锁」的场景,游戏中用的较少。
二、 muduo 读写锁 (ReadWriteLock / ReadLockGuard / WriteLockGuard)
1. 底层实现原理
muduo 的读写锁 = Linux pthread 库的pthread_rwlock_t (POSIX 标准读写锁) + RAII 封装,是 muduo 中性价比最高、游戏开发最常用的同步原语,没有之一。
- Linux 的
pthread_rwlock_t是内核态读写锁,核心规则:读共享、写独占; - 核心特性:多个线程可以同时加「读锁」并发读取,无竞争;只有写操作时会独占锁,所有读 / 写都会阻塞;默认读者优先(muduo 未修改底层属性)。
- 线程获取锁失败时,同样是让出 CPU、内核睡眠,直到锁被释放。
2. muduo 读写锁 核心源码实现
和互斥锁一样的设计思路,ReadWriteLock是锁本体,封装pthread_rwlock_t,配套ReadLockGuard(读锁守卫)、WriteLockGuard(写锁守卫),核心源码简化如下:
// muduo/base/ReadWriteLock.h
#include <pthread.h>
#include <boost/noncopyable.hpp>
class ReadWriteLock : boost::noncopyable {
public:
ReadWriteLock() { pthread_rwlock_init(&rwlock_, nullptr); }
~ReadWriteLock() { pthread_rwlock_destroy(&rwlock_); }
// 加读锁
void readLock() { pthread_rwlock_rdlock(&rwlock_); }
// 加写锁
void writeLock() { pthread_rwlock_wrlock(&rwlock_); }
// 解锁(读/写锁解锁用同一个接口)
void unlock() { pthread_rwlock_unlock(&rwlock_); }
private:
pthread_rwlock_t rwlock_;
};
// 读锁守卫:RAII自动加读锁/解锁
class ReadLockGuard : boost::noncopyable {
public:
explicit ReadLockGuard(ReadWriteLock& rwlock) : rwlock_(rwlock) { rwlock_.readLock(); }
~ReadLockGuard() { rwlock_.unlock(); }
private:
ReadWriteLock& rwlock_;
};
// 写锁守卫:RAII自动加写锁/解锁
class WriteLockGuard : boost::noncopyable {
public:
explicit WriteLockGuard(ReadWriteLock& rwlock) : rwlock_(rwlock) { rwlock_.writeLock(); }
~WriteLockGuard() { rwlock_.unlock(); }
private:
ReadWriteLock& rwlock_;
};
muduo::ReadWriteLock rwlock;
std::unordered_map<uint64_t, int64_t> boss_damage_map;
// 读操作:多线程并发,无阻塞
int64_t getPlayerDamage(uint64_t pid) {
muduo::ReadLockGuard lock(rwlock);
return boss_damage_map[pid];
}
// 写操作:独占执行,无竞争
void addPlayerDamage(uint64_t pid, int64_t damage) {
muduo::WriteLockGuard lock(rwlock);
boss_damage_map[pid] += damage;
}
3. 特性 & 使用场景
- 特性:完美适配读多写少的业务场景,读操作并发无锁竞争,性能比互斥锁高一个量级;写操作独占,开销和互斥锁一致。
- 游戏场景:跨服 BOSS 伤害统计、玩家战力查询、排行榜查询、公会数据读取、技能配置读取,这是游戏服务器的绝对主流场景,95% 的读多写少场景都用读写锁,也是你之前问到的「BOSS 击杀统计」的最优解。
三、 muduo 自旋锁 (SpinLock / SpinLockGuard)
1. 底层实现原理
muduo 的自旋锁 提供了两种实现版本,都是单机级自旋锁,核心特性一致:线程获取锁失败时,不会睡眠,而是在用户态循环重试(自旋),直到拿到锁。
自旋锁的本质:用 CPU 算力开销 换 内核态上下文切换开销,临界区越短,性能越高。
- 版本 1(默认推荐):基于 Linux pthread 库的
pthread_spinlock_t实现,这是 Linux 原生的自旋锁,轻量、高效、稳定,muduo 的SpinLock默认用这个实现; - 版本 2:基于 C++11 的
std::atomic_flag(原子标志位)实现CAS 自旋锁,纯用户态、无系统调用,性能极致,muduo 中也有该实现,适合极致性能场景。
2. muduo 自旋锁 核心源码实现
版本 1:muduo 默认实现 - 基于 pthread_spinlock_t
// muduo/base/SpinLock.h
#include <pthread.h>
#include <boost/noncopyable.hpp>
class SpinLock : boost::noncopyable {
public:
SpinLock() { pthread_spin_init(&spinlock_, PTHREAD_PROCESS_PRIVATE); }
~SpinLock() { pthread_spin_destroy(&spinlock_); }
void lock() { pthread_spin_lock(&spinlock_); }
void unlock() { pthread_spin_unlock(&spinlock_); }
private:
pthread_spinlock_t spinlock_;
};
// RAII自旋锁守卫
class SpinLockGuard : boost::noncopyable {
public:
explicit SpinLockGuard(SpinLock& lock) : lock_(lock) { lock_.lock(); }
~SpinLockGuard() { lock_.unlock(); }
private:
SpinLock& lock_;
};
muduo::SpinLock spinlock;
int player_kill_count = 0;
void addKillCount() {
muduo::SpinLockGuard lock(spinlock);
player_kill_count++; // 临界区只有1行,完美适配自旋锁
}
版本 2:muduo 的 CAS 自旋锁 - 基于 std::atomic_flag
这是纯用户态的自旋锁,无任何系统调用,性能天花板,也是 muduo 的高性能选型,核心是std::atomic_flag的test_and_set()原子操作(本质就是 CPU 的 CAS 指令):
// muduo/base/SpinLock.h (CAS版)
#include <atomic>
#include <boost/noncopyable.hpp>
class SpinLockCAS : boost::noncopyable {
public:
SpinLockCAS() : flag_(ATOMIC_FLAG_INIT) {}
~SpinLockCAS() = default;
void lock() {
// CAS自旋:循环重试,直到拿到锁
while (flag_.test_and_set(std::memory_order_acquire)) {}
}
void unlock() {
flag_.clear(std::memory_order_release);
}
private:
std::atomic_flag flag_; // 原子标志位,CAS核心
};
3. 特性 & 使用场景
- 特性:用户态自旋、无上下文切换、加锁解锁速度极快,是所有锁中性能最高的;缺点是自旋时会独占 CPU 核心,如果临界区过长 / 锁竞争激烈,会导致 CPU 使用率飙升到 100%,服务器卡顿。
- 游戏场景:临界区极致短(只有 1-3 行代码)、锁竞争频率极低的场景,比如:玩家击杀数累加、BOSS 被攻击次数统计、技能释放次数统计、简单标记位修改,也是你之前问到的「分段锁 + 自旋锁」的核心组合。
四、 muduo 对 CAS / 原子操作 的实现
1. 结论
- muduo 中没有单独封装「CAS 指令」:因为 CAS 是CPU 硬件级别的原子指令,C++11 已经通过
std::atomic模板类做了完美封装,muduo 直接复用 C++11 标准库的原子操作,不做重复造轮子; - muduo 的原子操作核心类:
muduo/base/Atomic.h中实现了AtomicIntegerT模板类,是对std::atomic<int32_t>/std::atomic<int64_t>的轻量封装,提供了get()/addAndGet()/incrementAndGet()等常用原子操作,底层就是CAS 自旋; - 本质:muduo 的原子操作 = C++11 std::atomic = CPU CAS 硬件指令,三者是层层封装的关系,性能无损耗。
2. muduo AtomicIntegerT 核心源码实现
这是 muduo 原子操作的核心,源码极简,核心就是CAS 自旋 + 循环重试,完美解决「多线程原子修改单一变量」的问题,游戏服务器的血量、金币、击杀数、伤害值全部用这个实现。
// muduo/base/Atomic.h
#include <atomic>
#include <boost/noncopyable.hpp>
template<typename T>
class AtomicIntegerT : boost::noncopyable {
public:
AtomicIntegerT() : value_(0) {}
// 获取当前值
T get() const {
return value_.load(std::memory_order_relaxed);
}
// 原子加delta,返回新值 【核心:CAS自旋】
T addAndGet(T delta) {
T oldVal = get();
// CAS自旋:失败则循环重试,直到原子操作成功
while (!value_.compare_exchange_weak(oldVal, oldVal + delta)) {}
return oldVal + delta;
}
// 原子自增1
T incrementAndGet() { return addAndGet(1); }
// 原子赋值
void set(T newValue) { value_.store(newValue); }
private:
std::atomic<T> value_;
};
// 常用类型typedef,游戏开发直接用
typedef AtomicIntegerT<int32_t> AtomicInt32;
typedef AtomicIntegerT<int64_t> AtomicInt64;
muduo::AtomicInt64 boss_remain_hp; // BOSS血量,原子整型
muduo::AtomicInt64 last_hit_player_id; // 最后一击玩家ID
void attackBoss(uint64_t pid, int64_t damage) {
int64_t old_hp = boss_remain_hp.get();
int64_t new_hp = old_hp - damage;
// CAS自旋:原子扣血,保证多线程无竞争
while (!boss_remain_hp.value_.compare_exchange_weak(old_hp, new_hp)) {
new_hp = old_hp - damage;
}
// 记录最后一击
if (new_hp <= 0 && old_hp > 0) {
last_hit_player_id.set(pid);
}
}
3. 特性 & 使用场景
- 特性:无锁、无阻塞、无系统调用、性能天花板,是单机级单一变量原子修改的最优解;支持多线程并发修改,无竞态问题;存在经典的ABA 问题(muduo 未处理,游戏业务中可通过版本号规避)。
- 游戏场景:BOSS 剩余血量扣减、玩家金币增减、击杀数 / 助攻数累加、技能冷却时间戳修改、最后一击玩家 ID 赋值,这是你之前问到的「多玩家攻击 BOSS」的核心实现,生产环境百分百用这个方案。
五、 muduo 与 分布式锁
1. 结论
muduo 库本身没有实现「分布式锁」
原因:muduo 是单机版的高性能网络库,设计目标是「单机多线程的高并发处理」,而分布式锁是分布式系统(多节点 / 多服务器) 的同步原语,解决的是「多台服务器之间的资源竞争」,属于业务层的分布式能力,不是 muduo 的设计范畴。
2.基于 muduo 的游戏服务器,如何实现【分布式锁】?
方案 1:基于 Redis 实现分布式锁
方案 2:基于 ZooKeeper 实现分布式锁
方案 3:基于数据库实现分布式锁
更多推荐



所有评论(0)