请从Linux,Windows角度进行解释?
我正在用C#编程,这两个术语会有所不同。请尽可能多地张贴带有示例等的内容。
谢谢
请从Linux,Windows角度进行解释?
我正在用C#编程,这两个术语会有所不同。请尽可能多地张贴带有示例等的内容。
谢谢
Answers:
对于Windows,关键部分的重量比互斥对象轻。
互斥可以在进程之间共享,但是总是导致对内核的系统调用,这会产生一些开销。
关键部分只能在一个进程中使用,但具有的优势是,它们仅在争用的情况下才切换到内核模式-非竞争性获取(通常是这种情况)非常快。在争用的情况下,它们进入内核以等待某些同步原语(例如事件或信号量)。
我编写了一个快速的示例应用程序,比较了两者之间的时间。在我的系统中,要进行1,000,000次无竞争的获取和释放,互斥体将占用一秒钟的时间。关键部分大约需要50毫秒才能完成1,000,000次采集。
这是测试代码,如果互斥是第一个或第二个,我会运行此代码并获得类似的结果,因此我们看不到任何其他效果。
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);
从理论上讲,关键部分是一段代码,它不能一次由多个线程运行,因为该代码访问共享资源。
甲互斥是用来保护临界区的算法(和有时的数据结构的名称)。
实际上,Windows中有许多互斥量实现。由于其实现的不同,它们的主要不同之处在于它们的锁定级别,范围,成本以及在不同争用级别下的性能。有关不同互斥体实现成本的图表,请参阅“ CLR由内而外-使用并发性实现可伸缩性 ”。
可用的同步原语。
该lock(object)
语句是使用Monitor
- 实现的-请参见MSDN以供参考。
近年来,对无阻塞同步进行了大量研究。目标是以无锁或无等待的方式实现算法。在这种算法中,一个流程可以帮助其他流程完成其工作,以便该流程最终可以完成其工作。因此,即使试图执行某些工作的其他进程挂起,一个进程也可以完成其工作。使用Usinig锁,它们不会释放锁并阻止其他进程继续。
除了其他答案,以下详细信息特定于Windows上的关键部分:
InterlockedCompareExchange
操作一样简单在Linux中,我认为它们具有“自旋锁”,其作用与具有旋转计数的关键部分类似。
关键部分和互斥对象不是特定于操作系统的,它们的多线程/多处理概念。
关键部分 是一段代码,只能在任何给定时间自行运行(例如,有5个线程同时运行,并且有一个名为“ critical_section_function”的函数可更新数组)……您不希望所有5个线程一次更新数组,因此,当程序运行critical_section_function()时,其他任何线程都不必运行critical_section_function。
Mutex * Mutex是一种实现关键部分代码的方法(将其像令牌一样考虑...线程必须拥有它才能运行critical_section_code)
互斥锁是线程可以获取的对象,从而阻止其他线程获取它。这是建议性的,不是强制性的;线程可以使用互斥体表示的资源而无需获取它。
关键部分是操作系统保证不中断的代码长度。用伪代码,它将像:
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
仅加2美分,关键部分定义为一种结构,对其进行的操作在用户模式上下文中执行。
ntdll!_RTL_CRITICAL_SECTION + 0x000 DebugInfo:Ptr32 _RTL_CRITICAL_SECTION_DEBUG + 0x004 LockCount:Int4B + 0x008递归计数:Int4B + 0x00c OwningThread:Ptr32无效 + 0x010 LockSemaphore:Ptr32无效 + 0x014 SpinCount:Uint4B
而互斥锁是在Windows对象目录中创建的内核对象(ExMutantObjectType)。互斥操作主要在内核模式下实现。例如,创建互斥锁时,您最终在内核中调用了nt!NtCreateMutant。
迈克尔的好答案。我为C ++ 11中引入的互斥锁类添加了第三项测试。结果有些有趣,并且仍然支持他对单个进程的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(请注意,我最近两个时间的比率与迈克尔的比率大致相当,但我的机器比迈克尔的年龄小至少四岁,因此您可以看到有证据表明2009年至2013年之间速度提高了,当XPS-8700推出时)。新的互斥锁类的速度是Windows互斥锁的两倍,但仍不到Windows CRITICAL_SECTION对象的十分之一。请注意,我仅测试了非递归互斥体。CRITICAL_SECTION对象是递归的(如果离开的次数相同,则一个线程可以重复输入它们)。
如果AC函数仅使用其实际参数,则称为可重入函数。
可重入函数可以同时被多个线程调用。
可重入函数示例:
int reentrant_function (int a, int b)
{
int c;
c = a + b;
return c;
}
非重入函数示例:
int result;
void non_reentrant_function (int a, int b)
{
int c;
c = a + b;
result = c;
}
C标准库strtok()不可重入,并且不能同时被2个或更多线程使用。
某些平台SDK带有strtok()的可重入版本,称为strtok_r();。
恩里科·米格洛尔(Enrico Migliore)