我已经读了很多遍,在这里和网上的任何地方,互斥锁比临界区/信号量/插入你的首选同步方法慢,但是我从来没有看到任何论文或研究或任何东西来支持这个说法。那么,这个想法从何而来?是神话还是现实?互斥锁真的很慢吗?
8yparm6h1#
在Jim Beveridge和Robert Wiener所著的《Win32中的多线程应用程序》一书中,它说:“锁定无主互斥锁所需的时间几乎是锁定无主临界区所需时间的100倍,因为临界区可以在用户模式下完成,而不涉及内核”在MSDN here上,它说“临界区对象为互斥同步提供了一种稍微更快、更有效的机制”
zzoitvuj2#
我不相信任何一个答案都触及到了他们为什么不同的关键点。互斥锁位于操作系统级别。存在一个命名互斥锁,并且可以从操作系统中的任何进程访问它(前提是它的ACL允许从所有进程访问)。临界区的速度更快,因为它们不需要系统调用进入内核模式,但是它们只在WITHIN一个进程内工作,你不能使用临界区锁定多个进程。所以根据你想要达到的目标和你的软件设计,你应该选择最合适的工具来完成这项工作。我还要向你指出,信号量与互斥体/临界区是分开的,因为它们的计数不同。信号量可以用来控制对资源的多个并发访问,其中互斥体/临界区要么被访问,要么不被访问。
tjjdgumg3#
CRITICAL_SECTION被实现为一个自旋锁,自旋计数有上限。请参见MSDN InitializeCriticalSectionAndSpinCount了解这一点。当自旋计数“过去”时,临界区锁定信号量(或任何实现它的内核锁)。所以在代码中它是这样工作的(不是真的工作,应该只是一个例子):
CRITICAL_SECTION s; void EnterCriticalSection( CRITICAL_SECTION* s ) { int spin_count = s.max_count; while( --spin_count >= 0 ) { if( InterlockedExchange( &s->Locked, 1 ) == 1 ) { // we own the lock now s->OwningThread = GetCurrentThread(); return; } } // lock the mutex and wait for an unlock WaitForSingleObject( &s->KernelLock, INFINITE ); }
因此,如果临界区只占用很短的时间,并且进入的线程只等待很少的“旋转”(周期),临界区可能会非常高效。但如果不是这样,临界区会浪费很多周期,什么也不做,然后福尔斯到内核同步对象。所以权衡是:
Mutex:获取/释放缓慢,但长“锁定区域”不会浪费周期临界截面:快速获取/发布无主“区域”,但浪费了有主部分的周期。
oxcyiej74#
是的,临界区更有效率。要想得到一个非常好的解释,请参阅"Windows上的并发编程"。简而言之:互斥体是一个内核对象,所以当你获取一个互斥体时,即使它是"空闲的",也总是会有一个上下文切换。2在这种情况下,一个临界区可以在没有上下文切换的情况下被获取,并且(在多核/处理器机器上)如果它被阻塞以防止昂贵的上下文切换,它甚至会旋转几个周期。
chhkpiq45#
互斥锁(至少在windows中)允许线程之外的不同进程之间的同步。这意味着必须做额外的工作来确保这一点。而且,正如Brian所指出的,使用互斥锁还需要切换到“内核”模式,这会导致另一次速度打击(我 * 相信 *,即推断,内核是这个进程间同步所必需的,但我没有任何证据支持我)。编辑:您可以找到对进程间同步here的显式引用,有关此主题的更多信息,请参阅Interprocess Synchronization
5条答案
按热度按时间8yparm6h1#
在Jim Beveridge和Robert Wiener所著的《Win32中的多线程应用程序》一书中,它说:“锁定无主互斥锁所需的时间几乎是锁定无主临界区所需时间的100倍,因为临界区可以在用户模式下完成,而不涉及内核”
在MSDN here上,它说“临界区对象为互斥同步提供了一种稍微更快、更有效的机制”
zzoitvuj2#
我不相信任何一个答案都触及到了他们为什么不同的关键点。
互斥锁位于操作系统级别。存在一个命名互斥锁,并且可以从操作系统中的任何进程访问它(前提是它的ACL允许从所有进程访问)。
临界区的速度更快,因为它们不需要系统调用进入内核模式,但是它们只在WITHIN一个进程内工作,你不能使用临界区锁定多个进程。所以根据你想要达到的目标和你的软件设计,你应该选择最合适的工具来完成这项工作。
我还要向你指出,信号量与互斥体/临界区是分开的,因为它们的计数不同。信号量可以用来控制对资源的多个并发访问,其中互斥体/临界区要么被访问,要么不被访问。
tjjdgumg3#
CRITICAL_SECTION被实现为一个自旋锁,自旋计数有上限。请参见MSDN InitializeCriticalSectionAndSpinCount了解这一点。
当自旋计数“过去”时,临界区锁定信号量(或任何实现它的内核锁)。
所以在代码中它是这样工作的(不是真的工作,应该只是一个例子):
因此,如果临界区只占用很短的时间,并且进入的线程只等待很少的“旋转”(周期),临界区可能会非常高效。但如果不是这样,临界区会浪费很多周期,什么也不做,然后福尔斯到内核同步对象。
所以权衡是:
Mutex:获取/释放缓慢,但长“锁定区域”不会浪费周期
临界截面:快速获取/发布无主“区域”,但浪费了有主部分的周期。
oxcyiej74#
是的,临界区更有效率。要想得到一个非常好的解释,请参阅"Windows上的并发编程"。
简而言之:互斥体是一个内核对象,所以当你获取一个互斥体时,即使它是"空闲的",也总是会有一个上下文切换。2在这种情况下,一个临界区可以在没有上下文切换的情况下被获取,并且(在多核/处理器机器上)如果它被阻塞以防止昂贵的上下文切换,它甚至会旋转几个周期。
chhkpiq45#
互斥锁(至少在windows中)允许线程之外的不同进程之间的同步。这意味着必须做额外的工作来确保这一点。而且,正如Brian所指出的,使用互斥锁还需要切换到“内核”模式,这会导致另一次速度打击(我 * 相信 *,即推断,内核是这个进程间同步所必需的,但我没有任何证据支持我)。
编辑:您可以找到对进程间同步here的显式引用,有关此主题的更多信息,请参阅Interprocess Synchronization