C#的lock关键字到C++的转换
Posted
技术标签:
【中文标题】C#的lock关键字到C++的转换【英文标题】:Conversion of lock keyword of C# to C++ 【发布时间】:2011-01-03 16:19:22 【问题描述】:如何在 C++ 中实现 C# 的 lock 关键字? c#中的代码如下:
try
lock (matrixLock)
ThisTransformation.get_Renamed(matrix);
【问题讨论】:
只是为了好玩,托管 C++ 确实有lock
,Visual Basic 有 SyncLock
,但实际上编译器正在将其转换为 Monitor.Enter(...)
和 Monitor.Exit
调用。
定位到哪个 .net 版本? lock
的含义会根据可用的 Monitor
重载而有所不同...
那个 try 与 catch 或 finally 相关联吗?捕获来自锁的异常是一种不好的代码气味。锁的重点是保持一致的共享状态不变量,异常的重点是某些不变量被意外违反。两者的结合似乎在酝酿中。如果 catch 被激活,那么锁只是被给了其他人,但 catch 还没有清除违反的状态。
【参考方案1】:
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 问题时,我正在寻找它。
:-)
一旦找到来源,我将使用正确的链接更新此答案。
编辑:找到源头!!!
FOR_EACH 和 LOCK,作者 Eric Niebler 和 Anson Tsao:http://drdobbs.com/184401723(或可打印版本 http://drdobbs.com/article/printableArticle.jhtml?articleId=184401723,恕我直言,格式更好) 安全布尔成语,作者 Bjorn Karlsson:http://www.artima.com/cppsource/safebool.html【讨论】:
【参考方案2】:您可以为此使用boost::mutex
和boost::scoped_lock
:
boost::mutex matrix_mutex;
// ...
try
boost::scoped_lock lock(matrix_mutex);
// ... everything in this scope is now locked
// ....
您可以使用宏和 for 循环为您提供 lock
关键字,但我强烈建议您不要这样做,因为这会破坏恰好使用 lock
作为标识符的代码。
【讨论】:
大多数 C++ 框架都有类似的东西 - Qt 中的QMutexLocker
,MFC 中的 CSingleLock
/CMultiLock
等【参考方案3】:
C# lock
关键字不是互斥体。相反,它调用Monitor::Enter()
试试这个。也可以看看MSDN reference。
// Request the lock, and block until it is obtained.
Monitor::Enter(m_inputQueue);
try
// Write your code here.
finally
// Ensure that the lock is released.
Monitor::Exit(m_inputQueue);
注意:此答案假定您的目标是 C++ CLI。
【讨论】:
另见 Eric Lippert 的文章,"Locks and exceptions do not mix."【参考方案4】:我一直在寻找来自 c# 背景的相同内容,并偶然发现了这个问题。我知道它被问到已经很多年了,但现在有了 c++11,你可以使用 std::lock_guard 和语法变得非常类似于c#
你在 c# 中的哪个位置
object syncObj;
...
lock(syncObj) ...
在 c++11 中,您可以使用 mutex 作为同步和 lock_guard,类似于 lock
std::mutex m;
...
std::lock_guard<std::mutex> lock(m);...
如果你
#define lock(mutex) std::lock_guard<std::mutex> lock(mutex);
那么它看起来真的是一样的:)
【讨论】:
【参考方案5】:这里有你需要的一切:Implementing a lock keyword in C++
C++ 没有 lock 关键字,但你可以自己制作一个。给定一个具有 Lock() 和 Unlock() 成员函数的 Mutex 类(为方便起见,可能还有一个 IsLocked())大多数 C++ 程序员会立即编写一个 AutoLock,有点像这样:
class AutoLock
public:
AutoLock(Mutex& m): m_mutex(m) m_mutex.Lock();
~AutoLock() m_mutex.Unlock();
operator bool() return m_mutex.IsLocked();
private:
Mutex& m_mutex;
;
这个东西的正常使用是这样的:
AutoLock lock(m_mutex);
// my protected code here
但是通过一个简单的预处理器技巧,您可以使语法与 C# 相同:
#define lock(x) if (!(AutoLock _l = x)); else
【讨论】:
天哪。这太可怕了。除了if (!(AutoLock _l = x))
在语法上不正确之外,如果你打算用 C++ 编写代码,你应该编写惯用的 C++。【参考方案6】:
C++ 没有 lock 关键字。你可以use a mutex。
【讨论】:
【参考方案7】:你不能,不存在这样的关键字,你会得到最接近的是 boost 的 scoped lock(可以使用 boost 互斥锁)。
【讨论】:
【参考方案8】:我会使用Boost synchronization library。如果由于某种原因你不能,我会像这样破解一些东西:
class CriticalSection
CRITICAL_SECTION m_cs;
public:
CriticalSection()
::InitializeCriticalSection(&m_cs);
~CriticalSection()
::DeleteCriticalSection(&m_cs);
void Lock()
::EnterCriticalSection(&m_cs);
void Unlock()
::LeaveCriticalSection(&m_cs);
;
class CriticalSectionLocker
CriticalSection& m_cs;
bool m_bLocked;
public:
CriticalSectionLocker(CriticalSection& cs, bool bLockNow = true) : m_cs(cs), m_bLocked(bLockNow)
if(bLockNow)
m_cs.Lock();
~CriticalSectionLocker()
if(m_bLocked)
m_cs.Unlock();
void Lock()
m_cs.Lock();
m_bLocked = true;
void Unlock()
m_cs.Unlock();
m_bLocked = false;
;
Caveat Emptor:此代码尚未通过编译器。 YMMV。
此代码允许您执行以下操作:
class SomeClass
CriticalSection m_cs;
SomeResource m_resource;
public:
void SomeOperation()
CriticalSectionLocker lock(m_cs);
m_resource.DoSomething();
;
锁被锁定在SomeClass::SomeOperation()
的范围内。如果您不再需要持有锁,您也可以通过调用其Unlock()
方法来释放锁。
此代码绝不是一概而论的。它可以通过模板超棒来整理,并使其足够通用以使用互斥锁和其他操作系统对象。但如果想去那里,我建议改用 boost 库。
【讨论】:
【参考方案9】:您可以使用std::lock_guard
这是该文档中的简化示例:
std::mutex resource_mutex;
...
std::lock_guard<std::mutex> lock(resource_mutex); // The variable name ("lock") doesn't matter since it is not supposed to be used anywhere else.
...
// safe to use resource here
...
【讨论】:
以上是关于C#的lock关键字到C++的转换的主要内容,如果未能解决你的问题,请参考以下文章