理解Semaphore及其用法详解
Mutex是一把鑰匙,一個人拿了就可進入一個房間,出來的時候把鑰匙交給隊列的第一個。一般的用法是用于串行化對critical section代碼的訪問,保證這段代碼不會被并行的運行。
Semaphore是一件可以容納N人的房間,如果人不滿就可以進去,如果人滿了,就要等待有人出來。對于N=1的情況,稱為binary semaphore。一般的用法是,用于限制對于某一資源的同時訪問。
Binary semaphore與
Mutex的差異:
在 有的系統中Binary semaphore與Mutex是沒有差異的。在有的系統上,主要的差異是mutex一定要由獲得鎖的進程來釋放。而semaphore可以由其它進程釋 放(這時的semaphore實際就是個原子的變量,大家可以加或減),因此semaphore可以用于進程間同步。Semaphore的同步功能是所有 系統都支持的,而Mutex能否由其他進程釋放則未定,因此建議mutex只用于保護critical section。而semaphore則用于保護某變量,或者同步。
關于semaphore和mutex的區別,網上有著名的廁所理論(http://koti.mbnet.fi/niclasw/MutexSemaphore.html):
Mutex:Is a key to a toilet. One person can have the key - occupy the toilet - at the time. When finished, the person gives (frees) the key to the next person in the queue.Officially: “Mutexes are typically used to serialise access to a section of re-entrant code that cannot be executed concurrently by more than one thread. A mutex object only allows one thread into a controlled section, forcing other threads which attempt to gain access to that section to wait until the first thread has exited from that section.”
Ref: Symbian Developer Library(A mutex is really a semaphore with value 1.)
Semaphore:
Is the number of free identical toilet keys. Example, say we have four toilets with identical locks and keys. The semaphore count - the count of keys - is set to 4 at beginning (all four toilets are free), then the count value is decremented as people are coming in. If all toilets are full, ie. there are no free keys left, the semaphore count is 0. Now, when eq. one person leaves the toilet, semaphore is increased to 1 (one free key), and given to the next person in the queue.
Officially: “A semaphore restricts the number of simultaneous users of a shared resource up to a maximum number. Threads can request access to the resource (decrementing the semaphore), and can signal that they have finished using the resource (incrementing the semaphore).”
Ref: Symbian Developer Library
所以,mutex就是一個binary semaphore (值就是0或者1)。但是他們的區別又在哪里呢?主要有兩個方面:
??? * 初始狀態不一樣:mutex的初始值是1(表示鎖available),而semaphore的初始值是0(表示unsignaled的狀態)。隨后的操 作基本一樣。mutex_lock和sem_post都把值從0變成1,mutex_unlock和sem_wait都把值從1變成0(如果值是零就等 待)。初始值決定了:雖然mutex_lock和sem_wait都是執行V操作,但是sem_wait將立刻將當前線程block住,直到有其他線程 post;mutex_lock在初始狀態下是可以進入的。
??? * 用法不一樣(對稱 vs. 非對稱):這里說的是“用法”。Semaphore實現了signal,但是mutex也有signal(當一個線程lock后另外一個線程 unlock,lock住的線程將收到這個signal繼續運行)。在mutex的使用中,模型是對稱的。unlock的線程也要先lock。而 semaphore則是非對稱的模型,對于一個semaphore,只有一方post,另外一方只wait。就拿上面的廁所理論來說,mutex是一個鑰 匙不斷重復的使用,傳遞在各個線程之間,而semaphore擇是一方不斷的制造鑰匙,而供另外一方使用(另外一方不用歸還)。
前面的實驗證明,mutex確實能夠做到post和wait的功能,只是大家不用而已,因為它是“mutex”不是semaphore。
下面給出一個例子:
要 讓一個thread在背景不斷的執行,最簡單的方式就是在該thread執行無窮回圈,如while(1) {},這種寫法雖可行,卻會讓CPU飆高到100%,因為CPU一直死死的等,其實比較好的方法是,背景平時在Sleep狀態,當前景呼叫背景時,背景馬 上被喚醒,執行該做的事,做完馬上Sleep,等待前景呼叫。當背景sem_wait()時,就是馬上處于Sleep狀態,當前景sem_post() 時,會馬上換起背景執行,如此就可避免CPU 100%的情形了。
/**//*(C) OOMusou 2006 http://oomusou.cnblogs.comFilename : pthread_create_semaphore.cppCompiler : gcc 4.10 on Fedora 5 / gcc 3.4 on Cygwin 1.5.21Description : Demo how to create thread with semaphorein Linux.Release : 12/03/2006Compile : g++-lpthread pthread_create_semaphore.cpp*/
#include <stdio.h> // printf(),
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <pthread.h> // pthread_create(), pthread_join()
#include <semaphore.h> // sem_init()sem_t binSem;void* helloWorld(void* arg);int main() {// Result for System callint res = 0;// Initialize semaphoreres = sem_init(&binSem, 0, 0);if (res) {printf("Semaphore initialization failed!!/n");exit(EXIT_FAILURE);}// Create threadpthread_t thdHelloWorld;res = pthread_create(&thdHelloWorld, NULL, helloWorld, NULL);if (res) {printf("Thread creation failed!!/n");exit(EXIT_FAILURE);}while(1) {// Post semaphoresem_post(&binSem);printf("In main, sleep several seconds./n");sleep(1);}// Wait for thread synchronizationvoid *threadResult;res = pthread_join(thdHelloWorld, &threadResult);if (res) {printf("Thread join failed!!/n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);
}void* helloWorld(void* arg) {while(1) {// Wait semaphoresem_wait(&binSem);printf("Hello World/n");}
}
編譯運行:
[root@localhost semaphore]# gcc semaphore.c-lpthread
[root@localhost semaphore]#./a.out
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
?
semaphore
信號量(Semaphore),有時被稱為信號燈,是在多線程環境下使用的一種設施, 它負責協調各個線程, 以保證它們能夠正確、合理的使用公共資源。
什么是信號量(Semaphore0
Semaphore分為單值和多值兩種,前者只能被一個線程獲得,后者可以被若干個線程獲得。
以一個停車場是運作為例。為了簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這是如果同時來了五輛車,看門人允許其中三輛不受阻礙的進入,然后放下車攔,剩下的車則必須在入口等待,此后來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知后,打開車攔,放入一輛,如果又離開兩輛,則又可以放入兩輛,如此往復。
在這個停車場系統中,車位是公共資源,每輛車好比一個線程,看門人起的就是信號量的作用。
更進一步,信號量的特性如下:信號量是一個非負整數(車位數),所有通過它的線程(車輛)都會將該整數減一(通過它當然是為了使用資源),當該整數值為零時,所有試圖通過它的線程都將處于等待狀態。在信號量上我們定義兩種操作: Wait(等待) 和 Release(釋放)。 當一個線程調用Wait等待)操作時,它要么通過然后將信號量減一,要么一自等下去,直到信號量大于一或超時。Release(釋放)實際上是在信號量上執行加操作,對應于車輛離開停車場,該操作之所以叫做“釋放”是應為加操作實際上是釋放了由信號量守護的資源。
實現
大家都知道,.Net Framework類庫中提供的線程同步設施包括:
Monitor, AutoResetEvent, ManualResetEvent,Mutex,ReadWriteLock和 InterLock。 其中 AutoResetEvent, ManualResetEvent,Mutex派生自WaitHandler,它們實際上是封裝了操作系統提供的內核對象。而其它的應當是在.Net虛擬機中土生土長的。顯然來自操作系統內核對象的設施使用起來效率要差一些。不過效率并不是我們這里要考慮的問題,我們將使用兩個 Monitor 和 一個ManualResetEvent 對象來模擬一個信號量。
代碼如下:
public class Semaphore
{
??? private ManualResetEvent waitEvent = new ManualResetEvent(false);
??? private object syncObjWait = new object();
??? private int maxCount = 1; file://最大資源數
??? private int currentCount = 0; file://當前資源數
??? public Semaphore()
{
}
?? public Semaphore( int maxCount )
{
?? this.maxCount = maxCount;
}
public bool Wait()
{
?? lock( syncObjWait ) file://只能一個線程進入下面代碼
?? {
????? ?bool waitResult = this.waitEvent.WaitOne(); file://在此等待資源數大于零
????? ?if( waitResult )
?????? {
???????? lock( this )
?????????? {
???????????? if( currentCount > 0 )
?????????????? {
????????????????? currentCount--;
????????????????? if( currentCount == 0 )
?????????????????? {
?????????????????????? this.waitEvent.Reset();
?????????????????? }
?????????????? }
???????? else
????????? {
?????????????? System.Diagnostics.Debug.Assert( false, "Semaphore is not allow current count < 0" );
????????? }
???? ?}
?}
?? return waitResult;
}
}
/** <summary>
/// 允許超時返回的 Wait 操作
/// </summary>
/// <param name="millisecondsTimeout"></param>
/// <returns></returns>
public bool Wait( int millisecondsTimeout )
{
???? lock( syncObjWait ) // Monitor 確保該范圍類代碼在臨界區內
??????? {
?????????? bool waitResult = this.waitEvent.WaitOne(millisecondsTimeout,false);
????????? if( waitResult )
????????? {
???????????? lock( this )
???????????? {
??????????????? if( currentCount > 0 )
??????????????? {
??????????????????? currentCount--;
??????????????????? if( currentCount == 0 )
???????????????????? {
??????????????????????? this.waitEvent.Reset();
????????????????????? }
????????????????? }
?????????????? else
?????????????? {
???????????????????? System.Diagnostics.Debug.Assert( false, "Semaphore is not allow current count < 0" );
???????????????? }
???????????? }
????????? }
????? return waitResult;
??? ?}
}
public bool Release()
{
??????? lock( this ) // Monitor 確保該范圍類代碼在臨界區內
?????? {
?????????? currentCount++;
???????????if( currentCount > this.maxCount )
????????? {
???????????? currentCount = this.maxCount;
???????????? return false;
??????????}
?????????? this.waitEvent.Set(); file://允許調用Wait的線程進入
??????? }
????? return true;
???? }
}
總結
以上是生活随笔為你收集整理的理解Semaphore及其用法详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win10网卡驱动如何重装? Win10
- 下一篇: 低耦合,高内聚的详解(绝对全面)