Semaphore, Mutex, Critical section, SpinLock, Event

拿到了一个offer,但是fail了一个也很想去的面试, 也许并不全是技术问题, 但是我竟然被critical section和mutex有何区别这个问题问倒了,使劲想觉得Linux和Vxworks里面似乎都没有啊,结果竟然是windows的。顺便把semaphore/spinlock/event/binary semaphore也带出来总结一下。

Critical Section

这是一个MS Windows的概念,它只能用于进程内的线程同步,由于不需要进入系统级(Mutex可以用于多进程同步因此必须是内核对象),它比较轻量,效率会高一点。对于semaphore/mutex一旦在调用WaitXXXX了(SleepXXX...)的时候OS就做Context   switch(除了核心态spinlock),有很多CPU时间被浪费,在x86处理器上这种switch一般要花费600个左右的 CPU指令周期。 

Windows相关函数是EnterCriticalSection()和LeaveCriticalSection(),注意如果不Leave,其他需要这个critical section的线程都会被挂起。如果用MFC库,用CCriticalSection类的Lock()和UnLock()就可以了。有人用以下代码测量,得到的结果是1,000,000 uncontended acquires and releases, a mutex takes over one second. A critical section takes ~50 ms

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);

临界区域的一个缺点就是:Critical Section不是一个核心对象,无法获知进入临界区的线程是生是死,如果进入临界区的线程挂了,没有释放临界资源,系统无法获知,而且没有办法释放该临界资源。

The 'fast' Windows equal of critical selection in Linux would be a futex, which stands for fast user space mutex. The difference between a futex and a mutex is that with a futex, the kernel only becomes involved when arbitration is required, so you save the overhead of talking to the kernel each time the atomic counter is modified. A futex can also be shared amongst processes, using the means you would employ to share a mutex.

Unfortunately, futexes can be very tricky to implement (PDF).

Mutex ("mutual exclusion" lock)

Mutex和critical section一样,用来保证同时只有一个线程进入某区域,通常用来实现对某单一资源的访问控制。Mutex可以设定time out,可以不像critical section一样死等。如果一个拥有Mutex的线程在返回之前没有调用ReleaseMutex(),那么这个Mutex就被舍弃了,但是当其他线程等待(WaitForSingleObject等)这个Mutex时,仍能返回,并得到一个WAIT_ABANDONED_0返回值。

A mutex (which stands for "mutual exclusion" lock) is a locking or synchronization object that allows multiple threads to synchronize access to shared resources. It is often used to ensure that shared variables are always seen by other threads in a consistent state.

In Windows, the mutexes are both named and un-named. The named mutex is shared between the threads of different process.

在MS Windows上API是CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()。用MFC库的CMutex类可以完成同样功能。

In Linux, the mutexes are shared only between the threads of the same process. To achieve the same functionality in Linux, a System V semaphore can be used (具体参考这篇文章).

支持POSIX库的系统(Linux/Unix)上有pthread_mutex_lock()和pthread_mutex_unlock()。

简单说CriticalSection is a user-mode component implemented by the Win32 subsystem, while Mutex is a kernel-mode component.
Practially, CriticalSection is much faster when there's no actual blocking (due to reduction in user-kernel mode switches), and probably slower when there is blocking (due to more complex implementation). Additionally, since a Mutex is represented by a HANDLE, you can wait on a mutex with a timeout, or with several other handles. Neither option is available with a Critical Section. Mutexes can be named and shared between processes, while CriticalSections are restricted to the threads of a single process.

 

Semaphore

特点是有计数,同时可以有N个线程可以进入一个区域。Windows上的API有CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()。或者用MFC的CSemaphore类。

VxWorks没有Mutex, 它提供三种Semaphore: binary, counting, mutex. 相关API是semBCreate, semMCreate, semCCreate, semDelete, semTake, semGive, semFlush

在VxWork上,Mutex会触发prority inheritance. If a higher priority task is waiting for a semaphore
taken a low priority task and the low priority task, its priority will be temporarily changed to the high priority task which is waiting.

 

Binary semaphore

在有的系统中Binary semaphore与Mutex是没有差异的。在有的系统上,主要的差异是mutex一定要由获得锁的进程来释放。而semaphore可以由其它进程释放(这时的semaphore实际就是个原子的变量,大家可以加或减),因此semaphore可以用于进程间同步。Semaphore的同步功能是所有系统都支持的,而Mutex能否由其他进程释放则未定,因此建议mutex只用于保护critical section。而semaphore则用于保护某变量,或者同步。

Event

Event可以用来实现observer模式。创建一个Event,然后用WaitForSingleObject()挂起等待其它线程点亮(set)这个Event。我经常用的一个伎俩是在用户态创建一个Event的句柄,然后通过DeviceIoControl()把它传到驱动里面去,当驱动收到外部总线传来的数据包就点亮这个Event。Windows的API是CreateEvent()     OpenEvent()    SetEvent()     WaitForSingleObject() WaitForMultipleObjects()。MFC库里有CEvent类。

Spin lock

spin lock是一个内核态概念。spin lock与semaphore的主要区别是spin lock是busy waiting,而semaphore是sleep。对于可以sleep的进程来说,busy waiting当然没有意义。对于单CPU的系统,busy waiting当然更没意义(没有CPU可以释放锁)。因此,只有多CPU的内核态非进程空间,才会用到spin lock。Linux kernel的spin lock在非SMP的情况下,只是关irq,没有别的操作,用于确保该段程序的运行不会被打断。其实也就是类似mutex的作用,串行化对 critical section的访问。但是mutex不能保护中断的打断,也不能在中断处理程序中被调用。而spin lock也一般没有必要用于可以sleep的进程空间。

 

转自:http://mach.debagua.com/archives/2010/0427_001126.html

 

阅读更多
个人分类: windows
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭