DMutexLock l(mutex_); 是一种在 C++ 中非常常见的加锁方式,它利用了 RAII (Resource Acquisition Is Initialization,资源获取即初始化) 的思想。这种技术可以极大地简化资源管理(比如锁、内存、文件句柄等),并能保证在任何情况下(包括异常发生时)资源都能被正确释放。

让我们来分解这行代码:

  1. DMutexLock 是什么? 通过分析RocksDB项目中的代码,我们可以找到它的定义。在 util/distributed_mutex.h 文件中,DMutexLock 是一个类型别名:

    // ... existing code ...
    #ifdef USE_FOLLY
    
    #include <folly/synchronization/DistributedMutex.h>
    
    namespace ROCKSDB_NAMESPACE {
    
    class DMutex : public folly::DistributedMutex {
    // ... existing code ...
    };
    using DMutexLock = std::lock_guard<folly::DistributedMutex>;
    
    }  // namespace ROCKSDB_NAMESPACE
    
    #else
    
    #include <mutex>
    
    #include "port/port.h"
    
    namespace ROCKSDB_NAMESPACE {
    
    using DMutex = port::Mutex;
    using DMutexLock = std::lock_guard<DMutex>;
    
    }  // namespace ROCKSDB_NAMESPACE
    
    #endif
    

    可以看到,无论是否定义了 USE_FOLLYDMutexLock 最终都是 std::lock_guard 的一个别名。std::lock_guard 是 C++ 标准库提供的一个模板类,专门用于实现 RAII 风格的互斥锁管理。

  2. DMutexLock l(mutex_); 如何工作? 这行代码声明了一个名为 l 的 DMutexLock (也就是 std::lock_guard) 类型的局部变量。

    • 加锁 (Acquisition): 当程序执行到这行代码时,会创建 l 这个对象。在创建过程中,std::lock_guard 的构造函数会被调用,它会立刻对传入的 mutex_ 对象进行加锁(调用 mutex_.lock())。
    • 解锁 (Release): 当 l 的生命周期结束时(例如,函数执行完毕返回,或者代码块 {...} 结束),l 对象会被销毁。此时,std::lock_guard 的析构函数会自动被调用,它会自动对 mutex_ 对象进行解锁(调用 mutex_.unlock())。
  3. 实例分析 让我们看看lru_cache.cc 中的一个例子:

    lru_cache.cc

    // ... existing code ...
    size_t LRUCacheShard::GetUsage() const {
      DMutexLock l(mutex_);
      return usage_;
    }
    // ... existing code ...
    
    • 当 GetUsage() 函数被调用时,DMutexLock l(mutex_); 被执行,mutex_ 被锁定。
    • 接下来,return usage_; 在锁的保护下安全地读取 usage_ 成员变量的值。
    • 当函数通过 return 退出时,局部变量 l 的作用域结束,它的析构函数被调用,从而自动释放 mutex_ 锁。

这种 RAII 模式的锁有两大好处:

  • 代码简洁,不易出错:你不需要在每个函数出口(包括多个 return 语句)都手动调用 unlock(),避免了忘记解锁导致的死锁问题。
  • 异常安全 (Exception Safety):如果在锁已经获取、但函数尚未返回时发生了异常,C++ 的栈回退 (stack unwinding) 机制会保证局部对象 l 的析构函数仍然被调用。这意味着即使有异常,锁也一定会被释放,极大地增强了代码的健壮性。
Logo

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

更多推荐