A. - 快速回答
假设您有一个Mutex 和一个Lock 对象(您的里程可能会有所不同):
#define MY_OWN_LOCK(mm_mutex) \
\
if(bool b_1227F2B8_136B_11E1_973D_7D0B4924019B = false) \
; \
else \
for(Lock lock_1227F2B8_136B_11E1_973D_7D0B4924019B(mm_mutex); \
!b_1227F2B8_136B_11E1_973D_7D0B4924019B; \
b_1227F2B8_136B_11E1_973D_7D0B4924019B = true)
可以用作:
Mutex mutex ;
void foo()
{
// not locked
MY_OWN_LOCK(mutex)
{
// locked
}
// not locked
}
B. - 详细解答
这取决于您将使用的库。
B.1 - 先决条件
假设你有:
- 一个
Mutex 对象,它有一个lock() 和一个unlock() 方法
- 一个
Lock对象,它有一个以Mutex为参数的构造函数,在构造时调用其lock()方法,在销毁时调用unlock()方法
所以,你有类似的东西:
class Mutex
{
public :
lock() ;
unlock() ;
// etc.
} ;
class Lock
{
Mutex & m_mutex ;
public :
Lock(Mutex & p_mutex) : m_mutex(p_mutex)
{ this->m_mutex.lock() ; }
~Lock()
{ this->m_mutex.unlock() ; }
// etc.
} ;
B.2 - 原始 C+ 使用
如果你不熟悉 C++ 的 RAII,你的代码会是这样的:
void foo()
{
// not locked
mutex.lock() ;
// locked !
mutex.unlock() ;
// not locked
}
这段代码太错误了,我永远不会讨论它(如果需要,谷歌“异常安全”)。
B.3 - 原始 C++ 使用
void foo()
{
// not locked
{
Lock lock(mutex) ;
// locked !
}
// not locked
}
B.4 - 宏增强 C++ 使用
使用以下宏:
#define LOCK(mm_mutex) \
\
if(bool b = false) \
; \
else \
for(Lock lock(mm_mutex); !b; b = true)
你会写:
void foo()
{
// not locked
LOCK(mutex)
{
// locked !
}
// not locked
}
B.5 - 为什么这么复杂?
大多数锁宏都依赖于锁对象来进行测试。这要么需要实现Safe Bool Idiom(这对于当前的使用来说太过分了),要么需要将锁定对象强制转换为bool,这给类带来了自己的(大量)缺陷。
在当前实现中,if 用于声明将控制for 的主体执行的布尔值,而for 本身用于声明Lock 对象本身。
我相信这种模式被称为“C++ 变量注入”。
B.6 - 性能?
请注意,您正在锁定某些内容,因此 mutex.lock() 和 mutex.unlock() 中的代码将比宏中的任何内容花费更多的周期。
在非优化构建中,if 和 for 跳转将显示(例如,在可视化调试器上逐步尝试),但在优化构建中,整个 if 和 for 将被优化掉(“原始 C++ 使用”和“宏增强 C++ 使用”生成的程序集没有区别。
B.7 - 注意!!!
上面的宏是为了教育目的而简化的。要在生产代码中使用它,您必须:
- “命名空间”宏名称(即在其前面加上某种唯一名称,作为
BOOST_FOREACH 宏的BOOST_ 部分)
- 使布尔值
b 和锁定lock 变量“唯一”以确保它们不会与用户代码冲突。我通常为此使用 GUID/UUID 后缀(例如 b_ABCD_ABCD_AB_ABCDEF 和 lock_ABCD_ABCD_AB_ABCDEF)
B.8 - 来源
我第一次在一篇文章中看到了这种模式(我相信是 Andrei Alexandrescu 的文章),事实上,当我偶然发现这个 SO 问题时,我正在寻找它。
:-)
一旦找到来源,我将使用正确的链接更新此答案。
编辑:找到源头!!!