首页 > 解决方案 > MFC WaitForSingleObject 和 CCriticalSection - 如何使用?

问题描述

我目前正在研究大量使用 MFC 类的遗留代码库。它CCriticalSection用作互斥锁,并WaitForSingleObject“锁定”该互斥锁。代码大致如下所示:

struct Foo {
  static CCriticalSection mutex;

  void doSomeWriting() {
     mutex.Lock();
     …
     mutex.Unlock();
  }

  void doSomeReading() {
     WaitForSingleObject(mutex, some_timeout);
     …
     // No unlocking here!
  }
};

我会提供一个 MWE,但如果要信任我的 Visual Studio,使用 MFC 的最小应用程序似乎需要几千行代码。

我目前正在Application Verifier下运行应用程序,它会标记WaitForSingleObject()调用并抱怨内部的句柄mutex为 NULL。

我知道我应该从代码中删除 CCriticalSection(实际上我们正在重构它以使用std::recursive_mutex),但我想至少知道原作者想用这个实现什么。

不幸的是,我找不到有关如何交互的WaitForSingleObject文档。CCriticalSection 文档根本没有提到CCriticalSectionWaitForSingleObject 文档也没有提到. 使用临界区的示例仅处理对象,显然需要以某种方式对其进行初始化。此外,在这些示例中未使用。WaitForSingleObjectCCriticalSectionCRITICAL_SECTIONWaitForSingleObject

我的问题

标签: c++multithreadingmfclocking

解决方案


Application Verifier [...] 标记WaitForSingleObject()调用并抱怨 mutex 内部的句柄是NULL.

应用程序验证器是正确的。这正是正在发生的事情。这也很明显,为什么会这样。不太明显的是,为什么 Microsoft 决定强制CCriticalSection进入CSyncObject类层次结构是个好主意。

让我们从基类开始,CSyncObject它看起来像这样(与问题没有直接关系的所有内容都被剥离):

struct CSyncObject {
    CSyncObject() : m_hObject{nullptr} {}
    operator HANDLE() const { return m_hObject; }
    HANDLE m_hObject;
}

有一个默认的 c'tor 初始化唯一的数据成员 ( m_hObject) 和一个转换运算符 ( operator HANDLE()),以便CSyncObject可以将类型的对象传递给需要类型参数的函数HANDLE(如WaitForSingleObject)。

除了公开的数据成员之外,这实际上只是一个非常标准的包装器,围绕着表示为HANDLE. 当我们看一下时,事情开始横向发展CCriticalSection

struct CCriticalSection : CSyncObject {  // <- Look ma, I'm a CSyncObject!
    CCriticalSection() { /* Initialize m_sect */ }
    operator CRITICAL_SECTION*() { return &m_sect; }
    CRITICAL_SECTION m_sect;
}

同样,公共数据成员无论如何都必须欣赏一致性。但是看,还有更多!CCriticalSection继承自CSyncObject,所以现在它也有一个m_hObject。正如您可能已经猜到的那样,它从不接触、读取或更改除基类'c'tor 初始化它之外的任何东西(你的NULL那个应用程序验证器告诉你)。

如果不继承 public ,这不会那么糟糕。这样 a现在可以在任何预期 a 的地方使用。比如说,。CCriticalSectionoperator HANDLE()CCriticalSectionHANDLEWaitForSingleObject

有了所有这些,WaitForSingleObject(mutex, some_timeout)编译,甚至运行而不会立即失败。当然,它没有通过参数验证,但是当您不检查返回值时......归结为只是一个非常冗长的无操作。它当然不会等待任何事情,也不会防止并发执行。它需要修复。

剩下的问题:

是否允许WaitForSingleObject上电?CCriticalSection

当然,正如我们所见。CCriticalSection假装足够硬,CSyncObject以便编译器不会介意。

是否有必要CCriticalSection在调用之前以某种方式初始化 a WaitForSingleObject?应用程序验证程序错误似乎表明了这一点。

是的。临界区需要使用InitializeCriticalSectionInitializeCriticalSectionAndSpinCount进行初始化。这并不能CRITICAL_SECTION成为WaitForSingleObject.

是否WaitForSingleObject锁定CCriticalSection?或者它只是阻塞直到关键部分被解锁?

不,但是EnterCriticalSection将取得所有权,这是通过调用LeaveCriticalSection来释放的。

如果它锁定:doSomeReading()我的示例中的方法是否不需要再次解锁关键部分?

如果它使用正确的 API 调用来获取所有权,它会。因为它什么都不做,所以也没有必要释放任何东西。

如果它不做锁定:那么上面的例子并不能保证在线程 B 忙于执行(在调用之后)doSomeWriting()时不会在线程 A 中启动,对吗?doSomeReading()WaitForSingleObject()

正确的。线程 A 和 B 可以doSomeWriting()同时进入。没有实现任何同步。


推荐阅读