代码之家  ›  专栏  ›  技术社区  ›  aJ.

关键部分总是更快吗?

  •  23
  • aJ.  · 技术社区  · 16 年前

    我正在调试一个多线程应用程序,发现了 CRITICAL_SECTION . 我找到了数据成员 LockSemaphore 关键部分是一个有趣的部分。

    看起来像 闭锁信号器 是自动重置事件(不是名称所建议的信号量),当线程第一次等待时,操作系统会自动创建此事件 Critcal Section 它被其他线程锁定。

    现在,我想知道关键部分是否总是更快?事件是内核对象,每个关键部分对象都与事件对象相关联,然后如何 Critical Section 与其他内核对象(如mutex)相比,速度更快吗?此外,内部事件对象实际上如何影响关键部分的性能?

    这是 关键部分 :

    struct RTL_CRITICAL_SECTION
    {
        PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
        LONG LockCount;
        LONG RecursionCount;
        HANDLE OwningThread;
        HANDLE LockSemaphore;
        ULONG_PTR SpinCount;
    };
    
    6 回复  |  直到 10 年前
        1
  •  36
  •   ChrisW    16 年前

    当他们说一个关键部分是“快速”的时候,他们的意思是“当一个部分还没有被另一个线程锁定时,获取它是很便宜的”。

    [注意,如果 已经被另一个线程锁定,那么它的速度几乎不重要。]

    它之所以快是因为在进入内核之前,它使用了 InterlockedIncrement 其中之一 LONG 场(也许在 LockCount field)如果成功,那么它认为锁是在没有进入内核的情况下获得的。

    这个 联锁装置 我认为API是在用户模式下作为“lock inc”操作码实现的…换言之,您可以获得一个未经测试的关键部分,而不需要进行任何到内核的环转换。

        2
  •  26
  •   Foredecker    16 年前

    在性能工作中,很少有东西属于“始终”类别:)如果您自己使用其他原语实现类似于操作系统关键部分的东西,那么在大多数情况下,实现的几率会更慢。

    回答问题的最佳方法是使用性能度量。操作系统对象的执行方式是 非常 取决于场景。例如,如果争用较低,关键部分一般被认为是“快速”的。如果锁定时间小于旋转计数时间,它们也被认为是快速的。

    要确定的最重要的事情是,关键部分的争用是否是应用程序中的一阶限制因素。如果不是,那么只需使用一个关键部分normaly并处理应用程序的主要瓶颈(或瓶颈)。

    如果关键部分性能是关键的,那么可以考虑以下内容。

    1. 仔细设置“热”关键部分的旋转锁定计数。如果性能是最重要的,那么这里的工作是值得的。记住,虽然spin锁确实避免了用户模式到内核的转换,但它会以惊人的速度消耗CPU时间-在spin期间,没有其他东西可以使用该CPU时间。如果锁保持足够长的时间,那么旋转线程将实际阻塞,从而释放CPU来执行其他工作。
    2. 如果您有读写器模式,那么考虑使用 Slim Reader/Writer (SRW) locks . 缺点是它们只在Vista和Windows Server 2008及更高版本的产品上可用。
    3. 你可能会使用 condition variables 使用关键部分最小化轮询和争用,只在需要时才唤醒线程。同样,Vista和Windows Server 2008及更高版本的产品也支持这些功能。
    4. 考虑使用 Interlocked Singly Linked Lists (slist)-这些是高效的和“无锁”的。更好的是,XP和Windows Server 2003及更高版本的产品支持它们。
    5. 检查您的代码——您可以通过重构一些代码并使用一个互锁操作或slist进行同步和通信,从而打破“热”锁。

    总之,具有锁争用的调优方案可能具有挑战性(但很有趣!)工作。关注于测量应用程序的性能,并了解您的热门路径。中的xperf工具 Windows Performance Tool kit 您的朋友在吗:)我们刚刚在Microsoft Windows SDK for Windows 7和.NET Framework 3.5 SP1中发布了4.5版( ISO is here , web installer here )您可以找到xperf工具的论坛 here . v4.5完全支持Win7、Vista、Windows Server 2008-所有版本。

        3
  •  4
  •   Community CDub    8 年前

    关键部分更快,但是 InterlockedIncrement / InterlockedDecrement 是更多。请参阅此实现使用示例 LightweightLock full copy .

        4
  •  3
  •   DougN    12 年前

    关键部分将旋转一小段时间(几毫秒),并不断检查锁是否空闲。旋转计数“超时”后,它将返回内核事件。因此,如果锁的持有者很快就退出,那么您就不必向内核代码进行昂贵的转换。

    edit:在我的代码中找到了一些注释:显然,MS堆管理器使用的spin计数为4000(整数增量,而不是ms)

        5
  •  1
  •   Adrian McCarthy    16 年前

    以下是一种方法:

    如果没有争用,那么与互斥体进入内核模式相比,自旋锁的速度确实很快。

    当存在争用时,CriticalSection比直接使用互斥体稍微贵一些(因为检测spinlock状态需要额外的工作)。

    所以它可以归结为加权平均值,其中权重取决于调用模式的具体情况。也就是说,如果你没有什么争议,那么关键部分就是大胜。另一方面,如果一直存在大量的争用,那么相对于直接使用互斥体,您将支付非常小的惩罚。但是在这种情况下,通过切换到互斥模式所获得的收益是很小的,所以您最好尝试减少争用。

        6
  •  1
  •   John Samuel    10 年前

    关键部分比互斥体快,原因是关键部分不是内核对象。这是当前进程的全局内存的一部分。互斥体实际上位于内核中,创建互斥体对象需要内核切换,但在关键部分不需要。即使关键部分很快,当线程将等待状态时,在使用关键部分时也会有一个内核切换。这是因为线程调度发生在内核端。