理解CRITICAL_SECTION

来源:互联网 发布:h5和java后端怎么交互 编辑:程序博客网 时间:2024/04/29 10:26

定义:

CRITICAL_SECTION fMutex;<span style="white-space:pre"></span>/* 声明一个临界区 */
::InitializeCriticalSection(&fMutex);   /* 初始化临界区 */::EnterCriticalSection(&fMutex);<span style="white-space:pre"></span>/* 进入临界区 */BOOL bResult = ::TryEnterCriticalSection(&fMutex);<span style="white-space:pre"></span>/* 尝试进入临界区 */::LeaveCriticalSection(&fMutex);<span style="white-space:pre"></span>/* 离开临界区 */
::DeleteCriticalSection(&fMutex);<span style="white-space:pre"></span>/* 销毁临界区 */

摘要:

临界区是一种防止多个进程同时执行一个特定代码段的机制,这一主题并没有引起太多关注,因而人们未能对齐深刻理解。在需要跟踪代码中的多线程处理的性能时,对windows中临界区的深刻理解非常有用。本文深入研究临界区的原理,以揭示在查找死锁和确认性能问题过程中有用信息。它还包含一个便利的实用工具程序,可以显示所有临界区及其当前状态。


在外面许多年的编程实战中,对于Win32临界区没有收到非常多的“under the hood”关注而感到非常奇怪。当然,您可能了解有关临界区初始化与使用的基本知识,但您是否曾经花费时间来深入研究winnt.h所定义的CRITICAL_SECTION结构呢?在这一结构中有非常有意义的好东西被长期忽略。我们将对此进行补充,并向您介绍一些很有意义的技巧,这些技巧对于跟踪那些难以察觉的多线程错误非常有用。更重要的是,使用我们的MyCriticalSections使用工具,可以明白如何对CRITICAL_SECTION进行微小的扩展,以提供非常有用的特性,这些特性可用于调试和性能调整。

老实说,作者们经常忽略CRITICAL_SECTION结构的部分原因在于它在以下两个主要WIN32代码中的实现由很大不同。

使用流程:

在将临界区传递给InitializeCriticalSection时,临界区即开始存在。初始化之后,代码即将临界区传递给EnterCriticalSection和LeaveCriticalSection API。一个线程自EnterCriticalSection中返回后,所有其他调用EnterCriticalSection的线程都将被阻止,直到第一个线程调用LeaveCriticalSection为止。最后,当不再需要该临界区时,一种良好的编码习惯是将其传递给DeleteCriticalSection。


深入研究:RTL_CRITICAL_SECION结构

即使您已经在日常工作中使用过临界区,您也非常可能并没有真正了解超过文档之外的内容。事实上存在很多非常容易掌握的内容。例如,人们很少知道一个进程的临界区是保存于一个链表中,并且可以对其进行枚举。实际上,WINDBG支持!locks命令,这一命令可以列出目标进程中的所有临界区。我们稍后将要谈到的实用工具也应用了临界区这一鲜为人知的特性。为了真正理解这一实用工具如何工作,有必要真正掌握临界区的内部结构。记着这一点,现在开始研究RTL_CRITICAL_SECTION结构。为了方便起见,将其结构列出如下:

typedef struct _RTL_CRITICAL_SECTION {    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;    //    //  The following three fields control entering and exiting the critical    //  section for the resource    //    LONG LockCount;    LONG RecursionCount;    HANDLE OwningThread;        // from the thread's ClientId->UniqueThread    HANDLE LockSemaphore;    ULONG_PTR SpinCount;        // force size on 64-bit systems when packed} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
以下各段对每个字段进行说明。

DebugInfo 此字段包含一个指针,指向系统分配的伴随结构,该结构的类型为RTL_CRITICAL_SECTION_DEBUG。这一结构中包含更多极具价值的信息。稍后进行研究。

LockCount 这是临界区中最重要的一个。它被初始化为数值-1;此数值等于或大于0

时,表示此临界区被占用。当其不等于-1时,OwnngThread字段(包含了拥有此临界区的线程ID)。此字段与(RecursionCount - 1)数值之前的差值表示有多少个其他线程在等待获得该临界区。

RecursionCount

此字段包含所有者线程已经获得该临界区的次数。如果该数值为零,下一次尝试获取该临界区的线程将会成功。

OwningThread 此字段包含当前占有此临界区的线程的线程标识符。此线程ID与GetCurrentThreadID之类的API所返回的ID相同。

LockSemaphore 此字段的命名不恰当,它实际上时一个自复位事件,而不是一个信号。它是一个内核对象句柄,用于通知操作系统:该临界区现在空闲。

SpinCount 仅用于多处理器系统。MSDN 
文档对此字段进行如下说明:“在多处理器系统中,如果该临界区不可用,调用线程将在对与该临界区相关的信号执行等待操作之前,旋转 dwSpinCount 
次。如果该临界区在旋转操作期间变为可用,该调用线程就避免了等待操作。”旋转计数可以在多处理器计算机上提供更佳性能,其原因在于在一个循环中旋转通常要快于进入内核模式等待状态。此字段默认值为零,但可以用 
InitializeCriticalSectionAndSpinCount API 将其设置为一个不同值

0 0