互斥和临界区别有什么区别?

请从Linux,Windows的angular度解释?

我在C#编程,这两个术语会有所作为。 请尽可能多地发布,例如…

谢谢

对于Windows,临界区域比互斥体轻。

互斥量可以在进程之间共享,但是总是会导致系统调用到内核,这有一些开销。

关键部分只能在一个进程中使用,但具有在争用情况下只能切换到内核模式的优势 – 非常规获取应该是常见的情况,速度非常快。 在争用的情况下,他们进入内核等待一些同步原语(如事件或信号量)。

我写了一个快速示例应用程序,比较两者之间的时间。 在我的系统上有一百万个无条件的获取和释放,一个互斥量超过一秒。 关键部分在1,000,000次采集中需要大约50ms的时间。

这里是testing代码,我运行这个,如果互斥量是第一或第二,得到相似的结果,所以我们没有看到任何其他效果。

 HANDLE mutex = CreateMutex(NULL, FALSE, NULL); CRITICAL_SECTION critSec; InitializeCriticalSection(&critSec); LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); LARGE_INTEGER start, end; // Force code into memory, so we don't see any effects of paging. EnterCriticalSection(&critSec); LeaveCriticalSection(&critSec); QueryPerformanceCounter(&start); for (int i = 0; i < 1000000; i++) { EnterCriticalSection(&critSec); LeaveCriticalSection(&critSec); } QueryPerformanceCounter(&end); int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart); // Force code into memory, so we don't see any effects of paging. WaitForSingleObject(mutex, INFINITE); ReleaseMutex(mutex); QueryPerformanceCounter(&start); for (int i = 0; i < 1000000; i++) { WaitForSingleObject(mutex, INFINITE); ReleaseMutex(mutex); } QueryPerformanceCounter(&end); int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart); printf("Mutex: %d CritSec: %d\n", totalTime, totalTimeCS); 

从理论上讲, 关键部分是一段代码,不能一次由多个线程运行,因为代码访问共享资源。

互斥锁是一种用于保护关键部分的algorithm(有时也是数据结构的名称)。

信号量和监视器是互斥量的常见实现。

实际上在Windows中有很多互斥体实现。 它们的主要区别在于其locking水平,范围,成本以及在不同层次上的performance。 请参阅CLR Inside Out – 使用并发性进行可伸缩性以获取不同互斥体实现的成本图表。

可用的同步原语。

  • 监控
  • 互斥
  • 信号
  • ReaderWriterLock
  • ReaderWriterLockSlim
  • 互锁

lock(object)语句是使用Monitor实现的 – 请参阅MSDN以供参考。

在过去的几年中,对非阻塞同步进行了很多研究。 目标是以无锁或等待方式实现algorithm。 在这样的algorithm中,一个过程可以帮助其他过程完成他们的工作,这样过程才能最终完成工作。 因此,即使其他尝试执行某些工作的进程挂起,进程也可以完成其工作。 Usinig锁,他们不会释放他们的锁,并阻止其他进程继续。

关键部分和互斥体不是操作系统特定的,它们的multithreading/多处理概念。

临界区是一段只能在任意给定时间自行运行的代码(例如,同时运行5个线程和一个名为“critical_section_function”的函数,用于更新数组…您不希望所有5个线程因此当程序运行critical_section_function()时,其他线程都不能运行其critical_section_function。

互斥体*互斥体是一种实现关键部分代码的方法(想象它就像一个令牌…线程必须拥有它来运行critical_section_code)

除了其他答案之外,以下详细信息仅针对Windows上的关键部分:

  • 在没有争用的情况下,获取关键部分与InterlockedCompareExchange操作一样简单
  • 临界区结构为互斥体留有余地。 它最初是未分配的
  • 如果临界区的线程之间存在争用,互斥将被分配和使用。 临界区的性能会降低到互斥量的性能
  • 如果您预期争用较高,则可以分配指定旋转计数的关键部分。
  • 如果在具有旋转计数的关键部分上存在争用,则尝试获取关键部分的线程将旋转(繁忙 – 等待)多个处理器周期。 这可以导致比睡眠更好的性能,因为执行上下文切换到另一个线程的周期数可能比拥有线程释放互斥体所花费的周期数多得多
  • 如果旋转计数到期,则会分配互斥量
  • 当拥有线程释放临界区时,需要检查是否分配了互斥量,如果是,则设置互斥量释放一个等待线程

在Linux中,我认为他们有一个“旋转locking”,用于与旋转计数的临界区相似的目的。

互斥体是一个线程可以获取的对象,阻止其他线程获取它。 这是咨询,不是强制性的; 一个线程可以使用互斥体代表的资源而不需要获取它。

关键部分是由操作系统保证不被中断的一段代码。 在伪代码中,它会是这样的:

 StartCriticalSection(); DoSomethingImportant(); DoSomeOtherImportantThing(); EndCriticalSection(); 

在Linux中,“快速”的Windows等于挑剔的select将会是一个代表快速用户空间互斥的代表。 futex和mutex之间的区别在于,使用futex时,内核只有在需要仲裁时才会涉及,因此每次修改primefaces计数器时都可以节省与内核通信的开销。 一个futex也可以在进程之间共享,使用你将用来共享互斥的手段。

不幸的是,futexes 实施起来可能非常棘手 (PDF)。

除此之外,在两个平台上都差不多。 您正在以一种(希望)不会导致饥饿的方式对共享结构进行primefaces,令牌驱动的更新。 剩下的只是完成这个的方法。

在Windows中,关键部分是本地进程。 互斥可以跨进程共享/访问。 基本上,关键部分要便宜得多。 不能专门针对Linux进行评论,但在某些系统上,它们只是针对同一事物的别名。

为了增加我的2美分,关键部分被定义为一个结构,对它们的操作是在用户模式下执行的。

  NTDLL!_RTL_CRITICAL_SECTION
    + 0x000 DebugInfo:Ptr32 _RTL_CRITICAL_SECTION_DEBUG
    + 0x004 LockCount:Int4B
    + 0x008 RecursionCount:Int4B
    + 0x00c OwningThread:Ptr32无效
    + 0x010 LockSemaphore:Ptr32 Void
    + 0x014 SpinCount:Uint4B

而互斥体是在Windows对象目录中创build的内核对象(ExMutantObjectType)。 互斥锁操作大部分是在内核模式下实现的。 例如,在创build互斥锁时,最终会在内核中调用nt!NtCreateMutant。

迈克尔很好的回答。 我已经为C ++ 11中引入的互斥类添加了第三个testing。 结果有些有趣,并且仍然支持他原来对单个进程的CRITICAL_SECTION对象的认可。

 mutex m; HANDLE mutex = CreateMutex(NULL, FALSE, NULL); CRITICAL_SECTION critSec; InitializeCriticalSection(&critSec); LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); LARGE_INTEGER start, end; // Force code into memory, so we don't see any effects of paging. EnterCriticalSection(&critSec); LeaveCriticalSection(&critSec); QueryPerformanceCounter(&start); for (int i = 0; i < 1000000; i++) { EnterCriticalSection(&critSec); LeaveCriticalSection(&critSec); } QueryPerformanceCounter(&end); int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart); // Force code into memory, so we don't see any effects of paging. WaitForSingleObject(mutex, INFINITE); ReleaseMutex(mutex); QueryPerformanceCounter(&start); for (int i = 0; i < 1000000; i++) { WaitForSingleObject(mutex, INFINITE); ReleaseMutex(mutex); } QueryPerformanceCounter(&end); int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart); // Force code into memory, so we don't see any effects of paging. m.lock(); m.unlock(); QueryPerformanceCounter(&start); for (int i = 0; i < 1000000; i++) { m.lock(); m.unlock(); } QueryPerformanceCounter(&end); int totalTimeM = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart); printf("C++ Mutex: %d Mutex: %d CritSec: %d\n", totalTimeM, totalTime, totalTimeCS); 

我的成绩是217,473和19(注意我后两次的比例与迈克尔的大致相当,但是我的机器比他小了至less四年,所以你可以看到2009年到2013年之间速度的增加,当XPS-8700出来)。 新的互斥体类速度是Windows互斥量的两倍,但仍不到Windows CRITICAL_SECTION对象速度的十分之一。 请注意,我只testing了非recursion互斥锁。 CRITICAL_SECTION对象是recursion的(一个线程可以重复input它们,只要它保留相同的次数)。