c++ - 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 文档根本没有提到CCriticalSection
,WaitForSingleObject 文档也没有提到. 使用临界区的示例仅处理对象,显然需要以某种方式对其进行初始化。此外,在这些示例中未使用。WaitForSingleObject
CCriticalSection
CRITICAL_SECTION
WaitForSingleObject
我的问题
- 是否允许
WaitForSingleObject
上电?CCriticalSection
- 是否有必要
CCriticalSection
在调用之前以某种方式初始化 aWaitForSingleObject
?应用程序验证程序错误似乎表明了这一点。 - 是否
WaitForSingleObject
锁定CCriticalSection
?或者它只是阻塞直到关键部分被解锁? - 如果它锁定:
doSomeReading()
我的示例中的方法是否不需要再次解锁关键部分? - 如果它不做锁定:那么上面的例子并不能保证在线程 B 忙于执行(在调用之后)
doSomeWriting()
时不会在线程 A 中启动,对吗?doSomeReading()
WaitForSingleObject()
解决方案
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 的地方使用。比如说,。CCriticalSection
operator HANDLE()
CCriticalSection
HANDLE
WaitForSingleObject
有了所有这些,WaitForSingleObject(mutex, some_timeout)
编译,甚至运行而不会立即失败。当然,它没有通过参数验证,但是当您不检查返回值时......归结为只是一个非常冗长的无操作。它当然不会等待任何事情,也不会防止并发执行。它需要修复。
剩下的问题:
是否允许
WaitForSingleObject
上电?CCriticalSection
当然,正如我们所见。CCriticalSection
假装足够硬,CSyncObject
以便编译器不会介意。
是否有必要
CCriticalSection
在调用之前以某种方式初始化 aWaitForSingleObject
?应用程序验证程序错误似乎表明了这一点。
是的。临界区需要使用InitializeCriticalSection或InitializeCriticalSectionAndSpinCount进行初始化。这并不能CRITICAL_SECTION
成为WaitForSingleObject
.
是否
WaitForSingleObject
锁定CCriticalSection
?或者它只是阻塞直到关键部分被解锁?
不,但是EnterCriticalSection将取得所有权,这是通过调用LeaveCriticalSection来释放的。
如果它锁定:
doSomeReading()
我的示例中的方法是否不需要再次解锁关键部分?
如果它使用正确的 API 调用来获取所有权,它会。因为它什么都不做,所以也没有必要释放任何东西。
如果它不做锁定:那么上面的例子并不能保证在线程 B 忙于执行(在调用之后)
doSomeWriting()
时不会在线程 A 中启动,对吗?doSomeReading()
WaitForSingleObject()
正确的。线程 A 和 B 可以doSomeWriting()
同时进入。没有实现任何同步。
推荐阅读
- javascript - api请求后如何显示结果?(js)
- html - 在 HTML 代码中找不到项目的 ID
- sql - 如何在不使用 sql 中的限制子句的情况下获取过去 6 周的数据?
- string - 需要帮助使用 masm 以 80x86 汇编语言连接两个字符串
- google-sheets - 谷歌表格/excel在行中重复高值->索引列问题
- python - 有没有一种好方法可以将 python 中的一系列 def 函数与 Tkinter 一个接一个地运行?
- java - 将指定区域的长纪元时间转换为 UTC 中的长纪元时间
- sql - 计算 AWS Athena 表中每个组的中位数
- java - 在 Spring Boot RestController 中区分带有查询参数的端点与没有查询参数的端点
- python - 如何为某个用户打开一个窗口 discord.py