【问题标题】:Conversion of lock keyword of C# to C++C#的lock关键字到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 还没有清除违反的状态。

标签: c# c++ visual-studio visual-studio-2008


【解决方案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 问题时,我正在寻找它。

:-)

一旦找到来源,我将使用正确的链接更新此答案。

编辑:找到源头!!!

【讨论】:

    【解决方案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。

    【讨论】:

    【解决方案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
                ...
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-09-23
              • 2011-01-22
              • 1970-01-01
              • 2018-06-19
              相关资源
              最近更新 更多