互斥鎖、條件變量、信號量淺析
互斥鎖與條件變量
- 條件變量是為了保證同步 條件變量用在多線程多任務同步的,一個線程完成了某一個動作就通過條件變量告訴別的線程,別的線程再進行某些動作(大家都在semtake的時候,就阻塞在哪里)。信號量可以看作是有計數的條件變量。
- 互斥鎖是為了保證互斥 互斥鎖是用在多線程多任務互斥的,一個線程占用了某一個資源,那么別的線程就無法訪問,直到這個線程unlock,其他的線程才開始可以利用這個資源。比如對全局變量的訪問,有時要加鎖,操作完了,再解鎖。互斥鎖可以看作是二值信號量。
- 有的時候互斥鎖和條件變量會同時使用的 條件變量通過允許線程阻塞和等待另一個線程發送信號的方法彌補了互斥鎖的不足。在發送信號時,如果沒有線程等待在該條件變量上,那么信號將丟失。
信號量
簡介
信號量可以看作是互斥鎖和條件變量的結合,而互斥鎖和條件變量則可以看作是信號量的一種特殊形式。
信號量:只要信號量的value大于0,其他線程就可以sem_wait成功,成功后信號量的value減一。若value值不大于0,則sem_wait阻塞,直到sem_post釋放后value值加1。
以下是信號燈(量)的一些概念:
- 信號燈與互斥鎖和條件變量的主要不同在于”燈”的概念,燈亮則意味著資源可用,燈滅則意味著不可用。如果說后兩中同步方式側重于**”等待”操作,即資源不可用的話,信號燈機制則側重于"點燈"**,即告知資源可用;
- 沒有等待線程的解鎖或激發條件都是沒有意義的,而沒有等待燈亮的線程的點燈操作則有效,且能保持燈亮狀態。
- 信號燈的應用除了燈亮/燈滅這種二元燈以外,也可以采用大于1的燈數,以表示資源數大于1,這時可以稱之為多元燈。
信號燈(量)API
創建和注銷
POSIX信號燈標準定義了有名信號燈和無名信號燈兩種,但LinuxThreads的實現僅有無名燈,同時有名燈除了總是可用于多進程之間以外,在使用上與無名燈并沒有很大的區別,因此下面僅就無名燈進行討論。
-
創建
int sem_init(sem_t *sem, int pshared, unsigned int value)
這是創建信號燈的API,其中
value
為信號燈的初值,pshared
表示是否為多進程共享而不僅僅是用于一個進程。LinuxThreads沒有實現多進程共享信號燈,因此所有非0值的pshared
輸入都將使sem_init()返回-1,且置errno
為ENOSYS
。初始化好的信號燈由sem
變量表征,用于以下點燈、滅燈操作。 -
int sem_destroy(sem_t * sem)
被注銷的信號燈
sem
要求已沒有線程在等待該信號燈,否則返回-1,且置errno
為EBUSY
。除此之外,LinuxThreads的信號燈注銷函數不做其他動作。
點燈和滅燈
-
點燈
int sem_post(sem_t * sem)
點燈操作將信號燈值原子地加1,表示增加一個可訪問的資源,
sem_post()
是唯一能用于異步信號處理函數的POSIX異步信號安全的API。 -
滅燈
int sem_wait(sem_t * sem)
int sem_trywait(sem_t * sem)
sem_wait()
為等待燈亮操作,等待燈亮(信號燈值大于0),然后將信號燈原子地減1,并返回。sem_trywait()
為sem_wait()
的非阻塞版,如果信號燈計數大于0,則原子地減1并返回0,否則立即返回-1,errno
置為EAGAIN
。
獲取燈值
int sem_getvalue(sem_t * sem, int * sval)
讀取sem
中的燈計數,存于*sval
中,并返回0。
辨析拾遺
- 互斥鎖必須是誰上鎖就由誰來解鎖,而信號量的wait和post操作不必由同一個線程執行。
- 互斥鎖是為上鎖而優化的;條件變量是為等待而優化的; 信號量既可用于上鎖,也可用于等待,因此會有更多的開銷和更高的復雜性。
- 互斥鎖,條件變量都只用于同一個進程的各線程間,而信號量(有名信號量)可用于不同進程間的同步。當信號量用于進程間同步時,要求信號量建立在共享內存區。
- 信號量有計數值,每次信號量post操作都會被記錄,而條件變量在發送信號時,如果沒有線程在等待該條件變量,那么信號將丟失。
Ref:
http://blog.chinaunix.net/uid-20671208-id-4935154.html
http://blog.chinaunix.net/uid-23061624-id-79936.html