Windows 同步技术-关键节对象(Critical Section)详解

Windows 关键节对象(Critical Section)详解

核心概念

关键节对象(Critical Section)是一种轻量级同步机制,​​仅限单进程内线程使用​​,提供高效互斥访问能力。与互斥体(Mutex)相比,关键节在单进程场景下性能更优,但无法跨进程同步,在所有的同步技术中,它不需要经过内核切换,所以效率也是最高的。

注意: 在xp开发的时代,关键段也被翻译为临界区,这两者是一样的。


关键节 vs 其他同步对象

特性 关键节 (Critical Section) 互斥体 (Mutex) 事件 (Event)
​作用域​ 单进程内线程同步 跨进程同步 通用同步(可跨进程/线程)
​所有权检测​ 无法检测放弃(Abandoned) 可检测放弃状态 无所有权概念
​效率​ 高(无系统调用开销) 中等(涉及内核对象) 低(依赖内核事件)
​初始化​ InitializeCriticalSection CreateMutex CreateEvent
​销毁​ DeleteCriticalSection CloseHandle CloseHandle

关键节操作流程

1. 初始化关键节

CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);         // 初始化
// 或带自旋计数的初始化(Windows XP+)
InitializeCriticalSectionAndSpinCount(&cs, 4000);

2. 进入和离开关健段

函数: EnterCriticalSection
若未被占用,线程立即获得所有权。
若被占用,可能自旋等待。

函数: LeaveCriticalSection
离开关键段
··· C
EnterCriticalSection(&cs);
// 执行需要同步的代码
LeaveCriticalSection(&cs);
···

3 销毁关键段

函数: DeleteCriticalSection
销毁关键段

DeleteCriticalSection(&cs);

关键段的定义

1. 结构定义

下面是伪代码定义的关键段:

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;  // 调试信息(仅调试版本有效)
    LONG LockCount;                         // 锁计数器(反映竞争状态)
    LONG RecursionCount;                    // 递归计数(同一线程重入次数)
    HANDLE OwningThread;                    // 当前持有锁的线程句柄
    HANDLE LockSemaphore;                   // 内核信号量(用于线程等待)
    ULONG_PTR SpinCount;                    // 自旋次数(用户态等待优化)
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

使用windbg查看的结果:

# 查看 CRITICAL_SECTION 内存布局
0:000> dt ntdll!_RTL_CRITICAL_SECTION
   +0x000 DebugInfo        : Ptr64 _RTL_CRITICAL_SECTION_DEBUG
   +0x008 LockCount        : Int4B
   +0x00c RecursionCount   : Int4B
   +0x010 OwningThread     : Ptr64 Void
   +0x018 LockSemaphore    : Ptr64 Void
   +0x020 SpinCount        : Uint8B

CRITICAL_SECTION 包含以下字段:

锁状态: 标识是否被占用(0 未锁定,1 锁定)。
拥有者线程ID: 当前持有锁的线程标识。
递归计数器: 支持同一线程多次获取锁(可重入性)。
等待队列: 阻塞线程的队列(可能关联内核事件对象)。

2. 用户态与内核态结合

用户态自旋:
使用原子操作(如 InterlockedCompareExchange)快速获取锁。
自旋次数由 SpinCount 参数控制(默认值为 0)。

内核态等待:
自旋超时后,调用内核对象(如事件)挂起线程,避免空转。

3. 递归支持

同一线程多次调用 EnterCriticalSection,递归计数器递增。
LeaveCriticalSection 递减计数器,归零时释放锁。

4. 线程唤醒机制

释放锁时,唤醒等待队列中的首个线程(非公平锁)。

代码演示

#include 
#include 

CRITICAL_SECTION cs;
int shared_value = 0;

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    EnterCriticalSection(&cs);
    shared_value++;
    printf("Thread %d: shared_value = %d\n", GetCurrentThreadId(), shared_value);
    LeaveCriticalSection(&cs);
    return 0;
}

int main() {
    InitializeCriticalSection(&cs);
    HANDLE threads[3];

    for (int i = 0; i < 3; i++) {
        threads[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
    }

    WaitForMultipleObjects(3, threads, TRUE, INFINITE);
    DeleteCriticalSection(&cs);
    return 0;
}

你可能感兴趣的:(Windows,同步技术,windows)