7

我正在尝试为 CRITICAL_SECTION 解锁代码添加一些调试检查,并尝试了以下操作:

...
  if (m_pCritSect) {
    ASSERT(m_pCritSect->OwningThread == GetCurrentThreadId());
    LeaveCriticalSection(m_pCritSect);
  }
}

通过调试 CRITICAL_SECTIONS(使用 VS 2005,主要在 WindowsXP 上),我“知道” (中定义的结构的OwningThread成员)的值是持有锁的线程的ID的值。RTL_CRITICAL_SECTIONwinnt.h

但是,线程 ID 由DWORD(typedef for unsigned long) 值表示,而此变量的类型HANDLE(typedef for void*) 需要areinterpret_cast才能使用HandleToULong宏 frombasetsd.h以使上述代码正常工作。

甚至MSDN 文档状态:

当第一个线程调用 EnterCriticalSection 例程时,(...) OwningThread 成为调用者的线程 ID。

那么到底为什么这被定义为一个HANDLE


编辑注意:我发现了一个声明,其中海报表明 HANDLE / DWORD-Id 不匹配是某些 Windows 内部的一些已知错误功能。所以也许这也是这里的情况:

GetCurrentThreadId 返回一个 DWORD,我在消息中将其发送给内核。PsLookupThreadByThreadId 采用 HANDLE 中的线程 ID,......

这是一个已知的 Windows API 错误(“已知”是因为我与相关的过滤器管理器 DEV 讨论了这个问题,因为它也出现在过滤器管理器中,因为 I/O 管理器 API 问题。)只要你不这样做t 有超过 5 亿左右的线程和进程(它们使用单​​个共享句柄表)你会没事的。也许到那是一个真正的问题时,我们会运行一些不同的东西。[ RE: ThreadId 处理 64 位?, 08 年 8 月 8 日 14:21,托尼·梅森]

4

2 回答 2

8

SDK 中名称以 RTL 或 Rtl 开头的任何标识符都是作为运行时层一部分的代码或声明,是将有据可查的 Winapi 与无证本机操作系统 api 结合起来的粘合剂。winapi 是一成不变的,本机操作系统会随着每个 Windows 版本发生重大变化。不可避免地,胶水也会发生变化。

winapi 是文档层,本机操作系统是无文档的。运行时层也没有记录在案,但随着时间的推移,它的一部分被揭露了。要么是因为它回填了 winapi 中缺少的功能。或者,在这种情况下,因为对于解决问题非常有用。然而,这样做的一个核心问题是,一旦声明被披露,微软就永远无法再更改它。因为这样做会破坏现有的程序,给客户带来很大的负担。

可以肯定的是,ThreadOwner 字段曾经在以前的 Windows 版本中真正拥有线程的句柄。请注意 LockSemaphore 也是如何误导的,它实际上是一个自动重置事件。来不及修复它,猫已经从袋子里出来了。

于 2012-10-01T16:19:49.873 回答
4

我认为主要原因是它是一个实现细节。如果在历史上的某个时候它真的是一个把手或类似的东西,我不会感到惊讶。

此外,我强烈建议不要在生产代码中使用内部成员,而且我并不孤单。如果您仔细观察,CRITICAL_SECTION您会发现使用的同步 API 不会在 MSDN 中记录为结构,而不是 RTL_CRITICAL_SECTION(其类型定义为 CRITICAL_SECTION)

存储在OwningThreadmember 中的值取自Thread Information Block的 CLIENT_ID 部分。在 CLIENT_ID 中,它被建模为 PVOID,这可能是它在 CRITICAL_SECTION 中以相同方式建模的原因:

typedef struct _CLIENT_ID
{
   PVOID UniqueProcess;
   PVOID UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
于 2012-10-01T14:49:55.877 回答