CMutex :: Lock vs. CSingleLock :: Lock

Jad*_*son 5 c++ mfc visual-c++ thread-synchronization

我被用来支持一些遗留代码,而且我看到一些让我困惑的事情.在代码的某些部分,我看到一个类实例使用CMutex实例来同步方法执行.例如

class CClassA : public CObject
{
public:
   void DoSomething();

private:
   CMutex m_mutex;
}

void CClassA::DoSomething()
{
   m_mutex.Lock();

   //...logic...

   m_mutex.Unlock();
}
Run Code Online (Sandbox Code Playgroud)

在同一项目的其他地方,我发现代码使用的是CSingleLock

class CClassB : public CObject
{
public:
   void DoSomething();

private:
   CCriticalSection m_crit;
}

void CClassB::DoSomething()
{
   CSingleLock lock(&m_crit);
   lock.Lock();

   //...logic...

   lock.Unlock();
}
Run Code Online (Sandbox Code Playgroud)

在查看MSDN文档以进行同步之后,似乎CClassB正在实施建议的方法,但我不清楚CClassA使用的实现中存在哪些危险.据我所知,这两种方法的唯一区别是CSingleLock具有RAII的优点,因此当执行退出范围时锁会自动释放.这两种实现都有其他好处/缺点吗?

AJG*_*G85 2

一般来说,互斥体可用于通过命名互斥体来控制跨进程的线程访问,而临界区仅用于同步同一进程空间中的线程访问。

如果不包装它们,这些类都不会真正获得 RAII 的好处,因为在这种情况下,您永远不需要显式调用 lock 或unlock。以使用 boost 互斥锁的这一点伪代码为例......

void DoSomething()
{
  // construction acquires lock on mutex
  boost::scoped_lock lock(&aBoostMutex);

  // ...

} // end scope - object is destroyed and lock is released
Run Code Online (Sandbox Code Playgroud)

现在我认为你应该避免CMutex, CCritalSection, CSemaphore, andCEvent因为这些实现有些损坏或者至少不如其他可用的库(如 boost)。例如:

  • 确定废弃互斥体的超时是不可能的,因为实现仅检查返回值而不检查原因。
  • 没有使用可重入锁,CSingleLock因此递归会导致问题。
  • 进程之间无法有命名事件

根据您的任务,您可能有机会放弃 Windows API 上的 MFC 包装器,并实现您自己的原子锁或使用诸如 boost 或 C++0x 之类的功能,这些功能不仅是std::mutex更好的实现,而且提供交叉- 平台支持。

  • 不幸的是,我被 MFC 困住了。我的问题不是关于 CMutex 和 CCriticalSection 之间的区别,而是更多关于 CSyncObject::Lock 和 CSingleLock::Lock 之间的区别。经过更多研究后,CSingleLock 的行为似乎与上面显示的 boost::scoped_lock 类似(在析构函数上调用 Unlock)。我没有意识到 CSingleLock 阻止了递归,我假设堆栈上的每个新实例都会独立管理。 (2认同)
  • 对于未来的读者,CSingleLock 有一个[构造函数](https://msdn.microsoft.com/en-us/library/fw63hszf.aspx) 参数,该参数将导致它在构造时被锁定。OP 的示例没有使用它,这违背了使用 CSingleLock 的最大原因。 (2认同)