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() 中的代码将比宏中的任何内容花费更多的周期。

在非优化构建中,iffor 跳转将显示(例如,在可视化调试器上逐步尝试),但在优化构建中,整个 iffor 将被优化掉(“原始 C++ 使用”和“宏增强 C++ 使用”生成的程序集没有区别。

B.7 - 注意!!!

上面的宏是为了教育目的而简化的。要在生产代码中使用它,您必须:

“命名空间”宏名称(即在其前面加上某种唯一名称,作为BOOST_FOREACH 宏的BOOST_ 部分) 使布尔值b 和锁定lock 变量“唯一”以确保它们不会与用户代码冲突。我通常为此使用 GUID/UUID 后缀(例如 b_ABCD_ABCD_AB_ABCDEFlock_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::mutexboost::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++的转换的主要内容,如果未能解决你的问题,请参考以下文章

C#中lock死锁

C#“lock”关键字:为啥语法需要对象?

C#中的lock关键字

Java进击C#——语法之线程同步

关于 C# 中的 lock 语句的困惑

C#中Lock的秘密