核心前提

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.hmuduo/base/SpinLock.hmuduo/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. 版本 1(默认推荐):基于 Linux pthread 库的 pthread_spinlock_t 实现,这是 Linux 原生的自旋锁,轻量、高效、稳定,muduo 的SpinLock默认用这个实现;
  2. 版本 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_flagtest_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:基于数据库实现分布式锁

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐