揭秘Android MMKV线程安全机制:从源码到实践的深度解析(9)

247 阅读17分钟

揭秘Android MMKV线程安全机制:从源码到实践的深度解析

在移动应用开发中,高性能且线程安全的数据存储是一项基础而关键的需求。MMKV作为一款专为移动平台设计的高性能键值存储框架,凭借其卓越的线程安全机制在Android开发中得到广泛应用。本文将深入剖析Android MMKV的源码,全面解析其线程安全机制的设计理念、实现细节及最佳实践。

一、线程安全机制概述

1.1 多线程环境下的数据竞争问题

在多线程环境中,多个线程同时访问和修改共享数据可能导致数据竞争(Data Race)问题,如脏读、不可重复读和幻读等。在存储系统中,这些问题可能导致数据不一致、程序崩溃等严重后果。

// 非线程安全的简单存储示例(仅作说明用)
public class UnsafeStorage {
    private Map<String, String> data = new HashMap<>();
    
    // 写入数据(非线程安全)
    public void put(String key, String value) {
        data.put(key, value);
        // 假设这里有其他操作,可能被多线程干扰
        if (data.size() % 10 == 0) {
            saveToDisk();
        }
    }
    
    // 读取数据(非线程安全)
    public String get(String key) {
        return data.get(key);
    }
    
    // 保存到磁盘
    private void saveToDisk() {
        // 将数据写入磁盘的操作
    }
}

在上述示例中,如果多个线程同时调用put方法,可能会导致data.size()计算错误,或者在保存到磁盘时数据不一致。

1.2 MMKV的线程安全设计目标

MMKV的线程安全机制旨在解决多线程环境下的数据竞争问题,确保:

  1. 数据一致性:所有线程看到的数据状态一致
  2. 操作原子性:读写操作是原子的,不会被其他线程中断
  3. 高性能:在保证线程安全的同时,不引入过大的性能开销
  4. 死锁避免:避免线程间因争夺锁资源而导致的死锁

1.3 线程安全机制的核心组件

MMKV的线程安全机制主要基于以下几个核心组件实现:

  1. 互斥锁(Mutex):用于保护关键资源,确保同一时间只有一个线程可以访问
  2. 读写锁(ReadWriteLock):允许多个线程同时读取,但写操作互斥
  3. 原子操作(Atomic Operations):用于轻量级同步,如引用计数、状态标志等
  4. 内存屏障(Memory Barrier):确保内存操作的顺序性和可见性

二、互斥锁机制的实现

2.1 基本互斥锁的使用

MMKV在关键操作中使用互斥锁来保护共享资源,确保同一时间只有一个线程可以执行关键代码段。

// MMKV.cpp
// 初始化互斥锁
MMKV::MMKV(const std::string &path, MMKVMode mode) : m_path(path), m_mode(mode) {
    // 初始化互斥锁属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    // 设置为递归锁,允许同一线程多次加锁
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    // 初始化互斥锁
    pthread_mutex_init(&m_lock, &attr);
    pthread_mutexattr_destroy(&attr);
    
    // 其他初始化操作
    initialize();
}

// 写入数据的操作,使用互斥锁保护
bool MMKV::setString(const std::string &key, const std::string &value) {
    // 加锁,确保同一时间只有一个线程可以执行以下代码
    SCOPED_LOCK(m_lock);
    
    // 计算数据大小
    size_t size = computeSize(key, value);
    // 确保有足够的空间
    if (!ensureSize(size)) {
        return false;
    }
    
    // 写入数据
    return writeString(key, value);
}

// 读取数据的操作,使用互斥锁保护
bool MMKV::getString(const std::string &key, std::string &value) {
    // 加锁,确保同一时间只有一个线程可以执行以下代码
    SCOPED_LOCK(m_lock);
    
    // 查找数据
    size_t offset = findKey(key);
    if (offset == INVALID_OFFSET) {
        return false;
    }
    
    // 读取数据
    return readString(offset, value);
}

2.2 递归锁的设计与应用

MMKV使用递归锁(Recursive Mutex)允许同一线程多次获取同一把锁而不会死锁。

// MMKV.cpp
// 递归锁的使用示例
bool MMKV::batchDelete(const std::vector<std::string> &keys) {
    // 第一次加锁
    SCOPED_LOCK(m_lock);
    
    // 批量删除操作
    bool result = true;
    for (const auto &key : keys) {
        // 可能会调用其他也需要加锁的方法
        if (!deleteKey(key)) {
            result = false;
        }
    }
    
    return result;
}

// 可能被batchDelete调用的方法
bool MMKV::deleteKey(const std::string &key) {
    // 同一线程再次加锁(递归锁允许这样做)
    SCOPED_LOCK(m_lock);
    
    // 查找数据
    size_t offset = findKey(key);
    if (offset == INVALID_OFFSET) {
        return false;
    }
    
    // 标记数据为已删除
    markAsDeleted(offset);
    return true;
}

递归锁的设计使得MMKV的内部方法调用更加灵活,避免了因锁的嵌套调用而导致的死锁问题。

2.3 锁的粒度控制

MMKV在设计上非常注重锁的粒度控制,只在必要的代码段加锁,减少锁的持有时间,从而提高并发性能。

// MMKV.cpp
// 优化锁粒度的示例
bool MMKV::containsKey(const std::string &key) {
    // 只在关键操作时加锁
    {
        SCOPED_LOCK(m_lock);
        // 查找数据
        return findKey(key) != INVALID_OFFSET;
    } // 锁在此处释放
    
    // 锁释放后执行其他操作
    notifyKeyCheck(key);
    return true;
}

在上述示例中,锁只保护了findKey这个关键操作,而后续的notifyKeyCheck方法调用则在锁外执行,这样可以减少锁的持有时间,提高并发度。

三、读写锁机制的实现

3.1 读写锁的基本原理

读写锁允许多个线程同时进行读操作,但在写操作时会互斥,确保写操作的原子性和数据一致性。

// ReadWriteLock.h
// 读写锁的实现
class ReadWriteLock {
private:
    pthread_rwlock_t m_rwlock; // POSIX读写锁
    
public:
    ReadWriteLock() {
        // 初始化读写锁
        pthread_rwlock_init(&m_rwlock, nullptr);
    }
    
    ~ReadWriteLock() {
        // 销毁读写锁
        pthread_rwlock_destroy(&m_rwlock);
    }
    
    // 获取读锁
    void lockRead() {
        pthread_rwlock_rdlock(&m_rwlock);
    }
    
    // 释放读锁
    void unlockRead() {
        pthread_rwlock_unlock(&m_rwlock);
    }
    
    // 获取写锁
    void lockWrite() {
        pthread_rwlock_wrlock(&m_rwlock);
    }
    
    // 释放写锁
    void unlockWrite() {
        pthread_rwlock_unlock(&m_rwlock);
    }
};

3.2 MMKV中的读写锁应用

MMKV在数据读写操作中使用读写锁来提高并发性能,允许多个线程同时读取数据。

// MMKV.cpp
// 使用读写锁的示例
class MMKV {
private:
    ReadWriteLock m_rwLock; // 读写锁
    // 其他成员变量
    
public:
    // 读取数据的操作,使用读锁
    bool getString(const std::string &key, std::string &value) {
        // 获取读锁,允许多个线程同时读取
        SCOPED_READ_LOCK(m_rwLock);
        
        // 查找数据
        size_t offset = findKey(key);
        if (offset == INVALID_OFFSET) {
            return false;
        }
        
        // 读取数据
        return readString(offset, value);
    }
    
    // 写入数据的操作,使用写锁
    bool setString(const std::string &key, const std::string &value) {
        // 获取写锁,确保同一时间只有一个线程可以写入
        SCOPED_WRITE_LOCK(m_rwLock);
        
        // 计算数据大小
        size_t size = computeSize(key, value);
        // 确保有足够的空间
        if (!ensureSize(size)) {
            return false;
        }
        
        // 写入数据
        return writeString(key, value);
    }
};

3.3 读写锁的公平性与性能平衡

MMKV在实现读写锁时,需要平衡公平性和性能:

// ReadWriteLock.h (改进版)
class ReadWriteLock {
private:
    pthread_rwlock_t m_rwlock;
    bool m_fairness; // 公平性标志
    
public:
    ReadWriteLock(bool fairness = false) : m_fairness(fairness) {
        // 初始化读写锁
        pthread_rwlockattr_t attr;
        pthread_rwlockattr_init(&attr);
        
        // 设置公平性
        if (fairness) {
            // 某些系统支持读写锁的公平性设置
            // 注意:并非所有系统都支持此选项
            pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
        }
        
        pthread_rwlock_init(&m_rwlock, &attr);
        pthread_rwlockattr_destroy(&attr);
    }
    
    // 其他方法保持不变
};

在实际应用中,MMKV默认使用非公平锁以获得更好的性能,因为在大多数场景下,读操作远远多于写操作,非公平锁可以减少线程切换开销。

四、原子操作与内存屏障

4.1 原子操作的基本实现

MMKV使用原子操作来实现轻量级同步,避免使用重量级锁带来的性能开销。

// Atomic.h
// 原子操作的实现
template <typename T>
class Atomic {
private:
    std::atomic<T> m_value; // C++11标准原子类型
    
public:
    Atomic(T initialValue = T()) : m_value(initialValue) {}
    
    // 获取原子值
    T load() const {
        return m_value.load(std::memory_order_acquire);
    }
    
    // 设置原子值
    void store(T newValue) {
        m_value.store(newValue, std::memory_order_release);
    }
    
    // 原子比较并交换
    bool compareExchange(T &expected, T desired) {
        return m_value.compare_exchange_weak(expected, desired, 
                                            std::memory_order_acq_rel, 
                                            std::memory_order_acquire);
    }
    
    // 原子递增
    T increment() {
        return m_value.fetch_add(1, std::memory_order_acq_rel) + 1;
    }
    
    // 原子递减
    T decrement() {
        return m_value.fetch_sub(1, std::memory_order_acq_rel) - 1;
    }
};

4.2 MMKV中的原子操作应用

MMKV在多个场景下使用原子操作,如引用计数、状态标志等。

// MMKV.cpp
// 使用原子操作的示例
class MMKV {
private:
    Atomic<int> m_refCount; // 引用计数,使用原子操作
    Atomic<bool> m_isClosed; // 关闭状态标志,使用原子操作
    
public:
    // 增加引用计数
    void retain() {
        m_refCount.increment();
    }
    
    // 减少引用计数,当计数为0时关闭
    void release() {
        if (m_refCount.decrement() == 0) {
            close();
        }
    }
    
    // 关闭操作
    void close() {
        // 使用原子操作检查是否已关闭
        bool expected = false;
        if (m_isClosed.compareExchange(expected, true)) {
            // 执行关闭操作
            doClose();
        }
    }
};

4.3 内存屏障的使用

MMKV使用内存屏障确保内存操作的顺序性和可见性,特别是在多线程环境下。

// MemoryBarrier.h
// 内存屏障的实现
class MemoryBarrier {
public:
    // 全内存屏障,确保所有内存操作的顺序
    static void full() {
        // 使用C++11的内存屏障
        std::atomic_thread_fence(std::memory_order_seq_cst);
    }
    
    // 获取内存屏障,确保后续的内存读取操作不会被重排到屏障之前
    static void acquire() {
        std::atomic_thread_fence(std::memory_order_acquire);
    }
    
    // 释放内存屏障,确保之前的内存写入操作不会被重排到屏障之后
    static void release() {
        std::atomic_thread_fence(std::memory_order_release);
    }
};

// MMKV.cpp
// 内存屏障的应用示例
bool MMKV::flush() {
    // 确保所有内存写入操作完成
    MemoryBarrier::release();
    
    // 执行文件同步操作
    bool result = fsync(m_fd) == 0;
    
    // 确保后续的内存读取操作能看到最新数据
    MemoryBarrier::acquire();
    
    return result;
}

五、线程安全的初始化与销毁

5.1 单例模式的线程安全实现

MMKV使用线程安全的单例模式来管理全局实例。

// MMKV.h
// 线程安全的单例模式实现
class MMKV {
private:
    static std::mutex s_instanceLock; // 单例锁
    static std::unordered_map<std::string, MMKV *> s_allInstances; // 所有实例
    
    // 私有构造函数
    MMKV(const std::string &path, MMKVMode mode);
    
    // 禁止拷贝和赋值
    MMKV(const MMKV &) = delete;
    MMKV &operator=(const MMKV &) = delete;
    
public:
    // 获取单例实例
    static MMKV *defaultMMKV(MMKVMode mode = MMKV_SINGLE_PROCESS) {
        static std::once_flag s_onceFlag;
        static std::string s_defaultPath = getDefaultMMKVPath();
        
        // 使用std::call_once确保只初始化一次
        std::call_once(s_onceFlag, []() {
            // 创建默认实例
            MMKV *kv = new MMKV(s_defaultPath, MMKV_SINGLE_PROCESS);
            s_allInstances[s_defaultPath] = kv;
        });
        
        // 从map中获取实例(已确保存在)
        return s_allInstances[s_defaultPath];
    }
    
    // 获取命名实例
    static MMKV *mmkvWithID(const std::string &mmapID, MMKVMode mode = MMKV_SINGLE_PROCESS) {
        std::lock_guard<std::mutex> lock(s_instanceLock);
        
        // 检查实例是否已存在
        auto it = s_allInstances.find(mmapID);
        if (it != s_allInstances.end()) {
            return it->second;
        }
        
        // 创建新实例
        std::string path = getMMKVPath(mmapID);
        MMKV *kv = new MMKV(path, mode);
        s_allInstances[mmapID] = kv;
        return kv;
    }
};

5.2 资源的线程安全销毁

MMKV在销毁时需要确保资源的正确释放,避免多线程环境下的竞态条件。

// MMKV.cpp
// 线程安全的资源销毁
MMKV::~MMKV() {
    // 确保在销毁前所有操作已完成
    SCOPED_LOCK(m_lock);
    
    // 关闭文件描述符
    if (m_fd >= 0) {
        close(m_fd);
        m_fd = -1;
    }
    
    // 解除内存映射
    if (m_ptr && m_size > 0) {
        munmap(m_ptr, m_size);
        m_ptr = nullptr;
        m_size = 0;
    }
    
    // 清理其他资源
    clearAllData();
}

// 安全关闭所有实例
void MMKV::closeAll() {
    std::lock_guard<std::mutex> lock(s_instanceLock);
    
    // 创建实例的副本,避免在遍历过程中修改map
    std::vector<MMKV *> instances;
    for (auto &pair : s_allInstances) {
        instances.push_back(pair.second);
    }
    
    // 关闭所有实例
    for (auto kv : instances) {
        delete kv;
    }
    
    // 清空实例map
    s_allInstances.clear();
}

六、多进程环境下的线程安全

6.1 进程间同步机制

在多进程环境下,MMKV使用文件锁来实现进程间的同步。

// FileLock.h
// 文件锁的实现
class FileLock {
private:
    int m_fd; // 文件描述符
    
public:
    explicit FileLock(int fd) : m_fd(fd) {}
    
    // 获取共享锁(读锁)
    bool lockShared() {
        struct flock lock;
        lock.l_type = F_RDLCK;  // 读锁
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;  // 锁定整个文件
        
        // 使用F_SETLKW进行阻塞式锁定
        return fcntl(m_fd, F_SETLKW, &lock) == 0;
    }
    
    // 获取排他锁(写锁)
    bool lockExclusive() {
        struct flock lock;
        lock.l_type = F_WRLCK;  // 写锁
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;  // 锁定整个文件
        
        // 使用F_SETLKW进行阻塞式锁定
        return fcntl(m_fd, F_SETLKW, &lock) == 0;
    }
    
    // 解锁
    bool unlock() {
        struct flock lock;
        lock.l_type = F_UNLCK;  // 解锁
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;  // 解锁整个文件
        
        return fcntl(m_fd, F_SETLK, &lock) == 0;
    }
};

6.2 多进程模式下的实现

MMKV在多进程模式下,通过文件锁和版本号机制确保数据的一致性。

// MMKV.cpp
// 多进程模式下的初始化
MMKV::MMKV(const std::string &path, MMKVMode mode) : m_path(path), m_mode(mode) {
    // 打开文件
    m_fd = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (m_fd < 0) {
        // 处理错误
        return;
    }
    
    // 初始化文件锁
    m_fileLock = new FileLock(m_fd);
    
    // 在多进程模式下,获取排他锁进行初始化
    if (mode & MMKV_MULTI_PROCESS) {
        m_fileLock->lockExclusive();
        
        // 初始化或加载文件内容
        initialize();
        
        // 释放排他锁
        m_fileLock->unlock();
    } else {
        // 单进程模式直接初始化
        initialize();
    }
}

// 多进程模式下的写操作
bool MMKV::setString(const std::string &key, const std::string &value) {
    // 在多进程模式下,获取排他锁
    if (m_mode & MMKV_MULTI_PROCESS) {
        m_fileLock->lockExclusive();
    }
    
    // 使用内部锁保护写操作
    SCOPED_LOCK(m_lock);
    
    // 执行写操作
    bool result = doSetString(key, value);
    
    // 增加版本号,表示数据已更新
    if (result) {
        m_version++;
        // 写入版本号到文件
        writeVersion();
    }
    
    // 释放排他锁
    if (m_mode & MMKV_MULTI_PROCESS) {
        m_fileLock->unlock();
    }
    
    return result;
}

// 多进程模式下的读操作
bool MMKV::getString(const std::string &key, std::string &value) {
    // 在多进程模式下,获取共享锁
    if (m_mode & MMKV_MULTI_PROCESS) {
        m_fileLock->lockShared();
    }
    
    // 使用内部锁保护读操作
    SCOPED_LOCK(m_lock);
    
    // 检查版本号,必要时重新加载数据
    checkVersionAndReload();
    
    // 执行读操作
    bool result = doGetString(key, value);
    
    // 释放共享锁
    if (m_mode & MMKV_MULTI_PROCESS) {
        m_fileLock->unlock();
    }
    
    return result;
}

七、线程安全的批量操作

7.1 批量操作的原子性保证

MMKV通过锁机制确保批量操作的原子性。

// MMKV.cpp
// 批量写入操作
bool MMKV::batchPut(const std::vector<std::pair<std::string, std::string>> &items) {
    // 获取写锁,确保操作的原子性
    SCOPED_WRITE_LOCK(m_rwLock);
    
    // 计算总大小
    size_t totalSize = 0;
    for (const auto &item : items) {
        totalSize += computeSize(item.first, item.second);
    }
    
    // 确保有足够空间
    if (!ensureSize(totalSize)) {
        return false;
    }
    
    // 批量写入数据
    for (const auto &item : items) {
        if (!writeString(item.first, item.second)) {
            return false;
        }
    }
    
    return true;
}

// 批量读取操作
bool MMKV::batchGet(const std::vector<std::string> &keys, 
                   std::vector<std::pair<std::string, std::string>> &values) {
    // 获取读锁,允许多个线程同时读取
    SCOPED_READ_LOCK(m_rwLock);
    
    // 批量读取数据
    for (const auto &key : keys) {
        std::string value;
        if (getString(key, value)) {
            values.push_back({key, value});
        }
    }
    
    return true;
}

7.2 事务机制的实现

MMKV通过临时存储和一次性提交实现简单的事务机制。

// MMKV.cpp
// 事务操作
class MMKVTransaction {
private:
    MMKV *m_mmkv;
    std::vector<std::pair<std::string, std::string>> m_pendingWrites;
    std::vector<std::string> m_pendingDeletes;
    bool m_active;
    
public:
    explicit MMKVTransaction(MMKV *mmkv) : m_mmkv(mmkv), m_active(true) {}
    
    // 添加写入操作
    void put(const std::string &key, const std::string &value) {
        if (!m_active) {
            return;
        }
        
        // 检查是否已在待删除列表中
        auto it = std::find(m_pendingDeletes.begin(), m_pendingDeletes.end(), key);
        if (it != m_pendingDeletes.end()) {
            m_pendingDeletes.erase(it);
        }
        
        // 添加到待写入列表
        m_pendingWrites.push_back({key, value});
    }
    
    // 添加删除操作
    void remove(const std::string &key) {
        if (!m_active) {
            return;
        }
        
        // 检查是否已在待写入列表中
        for (auto it = m_pendingWrites.begin(); it != m_pendingWrites.end(); ++it) {
            if (it->first == key) {
                m_pendingWrites.erase(it);
                break;
            }
        }
        
        // 添加到待删除列表
        m_pendingDeletes.push_back(key);
    }
    
    // 提交事务
    bool commit() {
        if (!m_active) {
            return false;
        }
        
        // 获取写锁
        SCOPED_WRITE_LOCK(m_mmkv->m_rwLock);
        
        // 执行所有待写入操作
        for (const auto &item : m_pendingWrites) {
            if (!m_mmkv->setString(item.first, item.second)) {
                return false;
            }
        }
        
        // 执行所有待删除操作
        for (const auto &key : m_pendingDeletes) {
            if (!m_mmkv->deleteKey(key)) {
                return false;
            }
        }
        
        // 事务完成
        m_active = false;
        return true;
    }
    
    // 回滚事务
    void rollback() {
        m_active = false;
        m_pendingWrites.clear();
        m_pendingDeletes.clear();
    }
    
    ~MMKVTransaction() {
        if (m_active) {
            rollback();
        }
    }
};

八、线程安全的缓存机制

8.1 缓存的线程安全设计

MMKV的缓存机制在多线程环境下需要保证数据一致性。

// MMKV.cpp
// 线程安全的缓存实现
class MMKV {
private:
    // 缓存项结构
    struct CacheItem {
        std::string value;
        time_t lastAccessTime;
        size_t accessCount;
    };
    
    // 缓存映射表
    std::unordered_map<std::string, CacheItem> m_cache;
    // 缓存锁
    mutable std::mutex m_cacheLock;
    // 最大缓存项数
    size_t m_maxCacheItems;
    
    // 从缓存获取数据
    bool getFromCache(const std::string &key, std::string &value) const {
        std::lock_guard<std::mutex> lock(m_cacheLock);
        
        auto it = m_cache.find(key);
        if (it != m_cache.end()) {
            // 更新访问时间和访问计数
            it->second.lastAccessTime = time(nullptr);
            it->second.accessCount++;
            value = it->second.value;
            return true;
        }
        
        return false;
    }
    
    // 添加数据到缓存
    void addToCache(const std::string &key, const std::string &value) {
        std::lock_guard<std::mutex> lock(m_cacheLock);
        
        // 检查缓存是否已满
        if (m_cache.size() >= m_maxCacheItems) {
            // 移除最不常用的项
            evictLeastUsedItem();
        }
        
        // 添加新项
        CacheItem item;
        item.value = value;
        item.lastAccessTime = time(nullptr);
        item.accessCount = 1;
        m_cache[key] = item;
    }
    
    // 移除最不常用的缓存项
    void evictLeastUsedItem() {
        if (m_cache.empty()) {
            return;
        }
        
        // 找到最不常用的项
        auto it = m_cache.begin();
        auto leastUsed = it;
        
        for (; it != m_cache.end(); ++it) {
            if (it->second.lastAccessTime < leastUsed->second.lastAccessTime ||
                (it->second.lastAccessTime == leastUsed->second.lastAccessTime &&
                 it->second.accessCount < leastUsed->second.accessCount)) {
                leastUsed = it;
            }
        }
        
        // 移除最不常用的项
        m_cache.erase(leastUsed);
    }
};

8.2 缓存与主存储的同步

MMKV确保缓存与主存储之间的数据一致性。

// MMKV.cpp
// 线程安全的读取操作,包含缓存逻辑
bool MMKV::getString(const std::string &key, std::string &value) {
    // 先尝试从缓存获取
    if (getFromCache(key, value)) {
        return true;
    }
    
    // 缓存未命中,从主存储读取
    SCOPED_READ_LOCK(m_rwLock);
    
    // 查找数据
    size_t offset = findKey(key);
    if (offset == INVALID_OFFSET) {
        return false;
    }
    
    // 读取数据
    if (!readString(offset, value)) {
        return false;
    }
    
    // 添加到缓存
    addToCache(key, value);
    
    return true;
}

// 线程安全的写入操作,包含缓存逻辑
bool MMKV::setString(const std::string &key, const std::string &value) {
    SCOPED_WRITE_LOCK(m_rwLock);
    
    // 计算数据大小
    size_t size = computeSize(key, value);
    // 确保有足够的空间
    if (!ensureSize(size)) {
        return false;
    }
    
    // 写入数据
    if (!writeString(key, value)) {
        return false;
    }
    
    // 更新缓存
    addToCache(key, value);
    
    return true;
}

九、线程安全的异常处理

9.1 异常安全的锁管理

MMKV使用RAII(资源获取即初始化)技术确保锁的异常安全性。

// LockGuard.h
// 基于RAII的锁管理类
template <typename LockType>
class LockGuard {
private:
    LockType &m_lock;
    bool m_locked;
    
public:
    explicit LockGuard(LockType &lock) : m_lock(lock), m_locked(true) {
        m_lock.lock();
    }
    
    ~LockGuard() {
        if (m_locked) {
            m_lock.unlock();
        }
    }
    
    // 禁止拷贝和赋值
    LockGuard(const LockGuard &) = delete;
    LockGuard &operator=(const LockGuard &) = delete;
    
    // 提前释放锁
    void unlock() {
        if (m_locked) {
            m_lock.unlock();
            m_locked = false;
        }
    }
};

// MMKV.cpp
// 使用LockGuard确保锁的异常安全
bool MMKV::deleteKey(const std::string &key) {
    // 使用LockGuard管理锁,确保锁在作用域结束时自动释放
    LockGuard<std::mutex> lock(m_lock);
    
    // 查找数据
    size_t offset = findKey(key);
    if (offset == INVALID_OFFSET) {
        return false;
    }
    
    // 执行可能抛出异常的操作
    if (!doDeleteKey(offset)) {
        return false;
    }
    
    // 从缓存中移除
    removeFromCache(key);
    
    return true;
}

9.2 异常情况下的资源保护

MMKV在异常情况下确保资源的正确释放和状态的一致性。

// MMKV.cpp
// 异常安全的文件操作
bool MMKV::writeString(const std::string &key, const std::string &value) {
    // 使用RAII管理文件操作
    FileOperationGuard guard(this);
    
    // 写入key长度
    if (!writeUInt32((uint32_t) key.size())) {
        throw std::runtime_error("Failed to write key length");
    }
    
    // 写入key
    if (!writeBytes(key.data(), key.size())) {
        throw std::runtime_error("Failed to write key");
    }
    
    // 写入value长度
    if (!writeUInt32((uint32_t) value.size())) {
        throw std::runtime_error("Failed to write value length");
    }
    
    // 写入value
    if (!writeBytes(value.data(), value.size())) {
        throw std::runtime_error("Failed to write value");
    }
    
    // 操作成功,提交
    guard.commit();
    return true;
}

// FileOperationGuard.h
// 文件操作的RAII保护类
class FileOperationGuard {
private:
    MMKV *m_mmkv;
    bool m_committed;
    size_t m_originalSize;
    
public:
    explicit FileOperationGuard(MMKV *mmkv) : m_mmkv(mmkv), m_committed(false) {
        // 保存原始大小,用于回滚
        m_originalSize = m_mmkv->m_usedSize;
    }
    
    ~FileOperationGuard() {
        if (!m_committed) {
            // 操作失败,回滚
            m_mmkv->rollback(m_originalSize);
        }
    }
    
    void commit() {
        m_committed = true;
    }
    
    // 禁止拷贝和赋值
    FileOperationGuard(const FileOperationGuard &) = delete;
    FileOperationGuard &operator=(const FileOperationGuard &) = delete;
};

十、线程安全机制的性能优化

10.1 锁粒度的优化

MMKV通过减小锁的粒度来提高并发性能。

// MMKV.cpp
// 优化锁粒度的示例
bool MMKV::containsKey(const std::string &key) {
    // 只在关键操作时加锁
    bool result;
    {
        SCOPED_READ_LOCK(m_rwLock);
        // 查找数据
        result = findKey(key) != INVALID_OFFSET;
    } // 锁在此处释放
    
    // 锁释放后执行其他操作
    if (result) {
        // 更新统计信息(不需要锁保护)
        updateHitCount(key);
    } else {
        updateMissCount(key);
    }
    
    return result;
}

10.2 无锁数据结构的应用

MMKV在某些场景下使用无锁数据结构来避免锁的开销。

// ConcurrentHashMap.h
// 线程安全的无锁哈希表
template <typename K, typename V>
class ConcurrentHashMap {
private:
    struct Node {
        K key;
        V value;
        std::atomic<Node *> next;
        
        Node(const K &k, const V &v) : key(k), value(v), next(nullptr) {}
    };
    
    std::vector<std::atomic<Node *>> m_buckets;
    size_t m_capacity;
    
public:
    explicit ConcurrentHashMap(size_t capacity = 1024) : m_capacity(capacity) {
        m_buckets.resize(capacity);
        for (size_t i = 0; i < capacity; ++i) {
            m_buckets[i].store(nullptr);
        }
    }
    
    // 无锁插入操作
    bool insert(const K &key, const V &value) {
        size_t index = hash(key) % m_capacity;
        Node *newNode = new Node(key, value);
        
        // 使用原子操作实现无锁插入
        Node *oldHead = m_buckets[index].load();
        do {
            newNode->next = oldHead;
        } while (!m_buckets[index].compare_exchange_weak(oldHead, newNode));
        
        return true;
    }
    
    // 无锁查找操作
    bool find(const K &key, V &value) const {
        size_t index = hash(key) % m_capacity;
        Node *node = m_buckets[index].load();
        
        while (node) {
            if (node->key == key) {
                value = node->value;
                return true;
            }
            node = node->next.load();
        }
        
        return false;
    }
    
    // 哈希函数
    size_t hash(const K &key) const {
        // 使用标准库的哈希函数
        return std::hash<K>()(key);
    }
};

十一、线程安全机制的测试与验证

11.1 多线程测试框架

MMKV使用专门的多线程测试框架来验证线程安全机制。

// ThreadTest.h
// 多线程测试框架
class ThreadTest {
private:
    std::vector<std::thread> m_threads;
    std::function<void(int)> m_testFunction;
    int m_threadCount;
    
public:
    ThreadTest(const std::function<void(int)> &testFunction, int threadCount = std::thread::hardware_concurrency())
        : m_testFunction(testFunction), m_threadCount(threadCount) {}
    
    // 运行测试
    void run() {
        // 创建并启动线程
        for (int i = 0; i < m_threadCount; ++i) {
            m_threads.emplace_back([this, i]() {
                m_testFunction(i);
            });
        }
        
        // 等待所有线程完成
        for (auto &thread : m_threads)