在阅读 WebRTC 源码过程中,经常可以看到 rtc::CritScope
相关的代码调用,例如:
1 | void RtpVideoSender::SetFecAllowed(bool fec_allowed) { |
笔者目前的主力语言还不是 C++,所以第一次见到这种加锁机制还挺新鲜的。事实上笔者刚开始甚至以为这只是创建了一个 cs
变量,然后什么都不做,不知道这样的代码有什么意义。
但这其实是 C++ 编程的小技巧。我们先来看看 rtc::CritScope
的具体实现:
1 | // CritScope 只有构造函数和析构函数两个定义; |
在 C++ 中,函数内部的局部变量会在该函数退出时进行析构(不论是否有异常)。通过在局部变量的构造函数中加锁,在析构函数中解锁,可以有效创造出一段函数生命周期内的临界区,而不用撰写类似 Java ReentrantLock 的 try-finally 释放锁的冗余代码:
1 | class X { |
更进一步来说,这其实是 C++ RAII(资源获取即初始化,Resource Acquisition Is Initialization)机制的一种使用场景。RAII 可以保证在释放资源时不受到异常退出的影响(即使发生了异常,也能正确释放资源);同时还能预防编码过程忘记释放资源的行为。在笔者看来,RAII 是比 Golang 的 defer 机制更加简洁的存在,哈哈。
从 WebRTC M86 (branch-heads/4240) 版本开始 rtc::CritScope
被废弃,改为使用新的 webrtc::Mutex
实现。这是因为前者为递归锁(可重入),存在一些难以解决的问题 [^1];需要改为非递归锁(不可重入)。关于递归锁的缺点,亦可参见笔者的 这篇博客。
[^1]: Issue 11567: Refactor webrtc to use a non-recursive CriticalSection