当它表示线程ID时,为什么CRITICAL_SECTION的OwningThread成员是HANDLE类型的?

时间:2012-10-01 14:31:20

标签: c++ windows winapi critical-section

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

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

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

但是,线程ID由DWORDunsigned long的typedef)值表示,而此变量的类型为HANDLEvoid*的typedef),需要 a {{ 1}} 使用来自reinterpret_cast的{​​{1}}宏来使上述代码生效。

即使是MSDN docs州:

  

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

那么为什么这个被定义为HandleToULong


编辑注意:我发现a statement海报显示HANDLE / DWORD-Id不匹配是某些Windows内部的一些已知错误。所以也许这就是这种情况:

  

GetCurrentThreadId返回一个DWORD,我将其发送到内核中   信息。 PsLookupThreadByThreadId在一个HANDLE中获取线程Id,...   ...

     

这是一个已知的Windows API错误(“已知”,因为我与之交谈过   关于此的相关过滤器管理器DEV,因为它显示在Filter中   管理器也是因为I / O Manager API问题。)只要你   没有超过五亿左右的线程和进程(他们   使用单个共享句柄表,你会没事的。也许到了时候   这是一个真正的问题,我们将运行不同的东西。 [ RE:ThreadId to HANDLE for 64 bit?,08 Aug 08 14:21,Tony Mason]

2 个答案:

答案 0 :(得分:8)

SDK中名称以RTL或Rtl开头的任何标识符都是代码或声明,它们是运行时层的一部分,是将记录良好的Winapi与未记录的本机操作系统api结合在一起的粘合剂。 winapi是一成不变的,每个Windows版本的本机操作系统都会发生巨大变化。不可避免地,胶水也会发生变化。

winapi是记录层,本机操作系统没有记录。运行时层也没有记录,但随着时间的推移,部分内容被揭示出来。无论是因为它回填了winapi中缺少的功能。或者,在这种情况下,因为真正有用的解决问题。然而,这样做的一个核心问题是,一旦宣布声明,微软就永远不会再改变它。因为这样做会破坏现有的程序,给客户带来很大的负担。

当然,ThreadOwner字段曾经真正拥有以前Windows版本中线程的句柄。注意LockSemaphore是如何误导的,它实际上是一个自动重置事件。为了解决这个问题太晚了,这只猫已经不在了。

答案 1 :(得分:4)

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

此外,我强烈建议不要在生产代码中使用内部成员,I am not alone。如果仔细观察,同步API使用CRITICAL_SECTION,您将无法将其记录为MSDN中的结构,而不是RTL_CRITICAL_SECTION(其类型定义为CRITICAL_SECTION)

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

typedef struct _CLIENT_ID
{
   PVOID UniqueProcess;
   PVOID UniqueThread;
} CLIENT_ID, *PCLIENT_ID;