C++中互斥锁和临界区的性能差异

3

我正在阅读这篇文章,它讨论了在给定测试用例下C#中使用临界区和互斥锁的性能差异。我想知道是否有进一步的文档可以说明C++应用程序中各种锁类的性能开销,特别是在运行于Windows 32或64位平台上的MFC。

我之所以问这个问题是因为广泛的自动化测试中,分析器的结果显示很多时间都花费在互斥锁代码中。我试图弄清楚其中多少是合理的等待资源可用的延迟,以及多少是由于实现和锁定结构的具体细节导致的。我只处理单个进程,其中包括多个线程,并考虑改用临界区。长期的自动化测试表明,我不需要互斥锁类提供的超时功能。

因此,我的问题是,是否有人了解与不同Windows平台上的不同MFC锁定机制的性能开销相关的参考文献?


2
你所链接的问题的答案同样适用于MFC。因为像C#一样,MFC类(如CMutexCCriticalSection)只是对应Win32功能的包装器。 - Christian Rau
谢谢Christian,我有点预料到这一点,但是我想知道结果是否与语言、硬件等有关,以及是否有任何与性能相关的硬信息。根据下面Mark Ingram的回答,微软文档建议使用临界区仅仅“稍微快一点”的说法似乎是误导性的。 - SmacL
3个回答

6
据我所了解,Win32互斥量是完整的内核对象。这意味着对互斥量的任何调用都将涉及系统调用。这通常会使缓存失效,因此可能非常昂贵。
临界区是用户端对象,在没有竞争的情况下不使用内核。这可能是使用x86 LOCK汇编指令或类似指令来保证原子性。由于没有进行系统调用,因此速度更快,但因为它不是内核对象,所以无法从另一个进程访问临界区。

3
几乎了解。我认为你所说的“...来自另一个线程”实际上是指“...来自另一个进程。” - RobH
谢谢提供的信息。听起来我需要在各种平台和条件下自己运行一些测试。 - SmacL
1
正确的想法,但是你对LOCK指令的引用虽然有点正确,但有些误导人。互斥锁的用户空间方面通常使用“比较和交换”和“原子增量”函数来完成。因此,可以查看是否存在争用而无需调用内核。如果没有争用,则不需要内核调用。如果存在争用,则需要内核调用以等待/释放互斥锁。Linux的futex系统调用的手册是获取此类系统详细信息(或pthread互斥锁代码)的好地方。 - edA-qa mort-ora-y
我更熟悉ARM指令集。对于armv5,您可以使用SWP指令,而对于armV6及更高版本,您可以使用LDREX和STREX指令。请参考:http://www.codemaestro.com/reviews/8 - doron

1
在Windows中,关键区域(Critical Sections)和互斥量(Mutexes)之间的关键区别在于您可以创建一个命名互斥量并从多个进程中使用它,而无法从另一个进程中访问一个进程的关键区域。
互斥量可在多个进程中使用的结果是必须由内核控制对其的访问。

1

阅读来自微软的以下支持文章: http://support.microsoft.com/kb/105678

临界区和互斥量提供非常相似的同步功能,但是临界区只能被一个进程的线程使用。在选择单个进程中要使用哪种方法时,需要考虑两个方面:

速度。 同步概述中提到了关于临界区的以下内容:

... 临界区对象提供了一种略微更快、更有效的互斥同步机制。 临界区使用特定于处理器的测试和设置指令来确定互斥。

死锁。 同步概述中提到了关于互斥量的以下内容:

如果线程终止而没有释放它对互斥对象的所有权,则认为该互斥对象已被放弃。等待线程可以获取已放弃的互斥对象的所有权,但等待函数的返回值指示该互斥对象已被放弃。 WaitForSingleObject()将针对已被放弃的互斥对象返回WAIT_ABANDONED。 但是,保护互斥对象的资源处于未知状态。
无法确定是否已放弃关键部分。

“稍微快一点”这个术语让我很感兴趣,因为迈克尔在链接的帖子中的回答表明要快超过20倍。 - SmacL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接