訊號量與鎖的差別

2021-07-11 05:38:50 字數 3187 閱讀 2866

訊號量用在多執行緒多工同步的,乙個執行緒完成了某乙個動作就通過訊號量告訴別的執行緒,別的執行緒再進行某些動作(大家都在semtake的時候,就阻塞在 **)。而互斥鎖是用在多執行緒多工互斥的,乙個執行緒占用了某乙個資源,那麼別的執行緒就無法訪問,直到這個執行緒unlock,其他的執行緒才開始可以利用這 個資源。比如對全域性變數的訪問,有時要加鎖,操作完了,在解鎖。有的時候鎖和訊號量會同時使用的」

也就是說,訊號量不一定是鎖定某乙個資源,而是流程上的概念,比如:有a,b兩個執行緒,b執行緒要等a執行緒完成某一任務以後再進行自己下面的步驟,這個任務 並不一定是鎖定某一資源,還可以是進行一些計算或者資料處理之類。而執行緒互斥量則是「鎖住某一資源」的概念,在鎖定期間內,其他執行緒無法對被保護的資料進 行操作。在有些情況下兩者可以互換。

兩者之間的區別:

作用域

訊號量: 程序間或執行緒間(linux僅執行緒間)

互斥鎖: 執行緒間

上鎖時

訊號量: 只要訊號量的value大於0,其他執行緒就可以sem_wait成功,成功後訊號量的value減一。若value值不大於0,則sem_wait阻塞,直到sem_post釋放後value值加一

互斥鎖: 只要被鎖住,其他任何執行緒都不可以訪問被保護的資源

成功後否則就阻塞

以下是訊號燈(量)的一些概念:

訊號燈與互斥鎖和條件變數的主要不同在於」燈」的概念,燈亮則意味著資源可用,燈滅則意味著不可用。如果說後兩中同步方式側重於」等待」操作,即資 源不可用的話,訊號燈機制則側重於點燈,即告知資源可用;沒有等待執行緒的解鎖或激發條件都是沒有意義的,而沒有等待燈亮的執行緒的點燈操作則有效,且能保持 燈亮狀態。當然,這樣的操作原語也意味著更多的開銷。

訊號燈的應用除了燈亮/燈滅這種二元燈以外,也可以採用大於1的燈數,以表示資源數大於1,這時可以稱之為多元燈。

1. 建立和 登出

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的訊號燈 登出函式不做其他動作。

2. 點燈和滅燈

int sem_post(sem_t * sem)

點燈操作將訊號燈值原子地加1,表示增加乙個可訪問的資源。

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。

3. 獲取燈值

int sem_getvalue(sem_t * sem, int * sval)

讀取sem中的燈計數,存於*sval中,並返回0。

4. 其他

sem_wait()被實現為取消點,而且在支援原子」比較且交換」指令的體系結構上,sem_post()是唯一能用於非同步訊號處理函式的posix非同步訊號 安全的api。

----------------------------

執行緒同步:何時互斥鎖不夠,還需要條件變數?

假設有共享的資源sum,與之相關聯的mutex 是lock_s.假設每個執行緒對sum的操作很簡單的,與sum的狀態無關,比如只是sum++.那麼只用mutex足夠了.程式設計師只要確保每個執行緒操作 前,取得lock,然後sum++,再unlock即可.每個執行緒的**將像這樣

add()

如果操作比較複雜,假設執行緒t0,t1,t2的操作是sum++,而執行緒t3則是在sum到達100的時候,列印出一條資訊,並對sum清零. 這種情況下,如果只用mutex, 則t3需要乙個迴圈,每個迴圈裡先取得lock_s,然後檢查sum的狀態,如果sum>=100,則列印並清零,然後unlock.如果sum& lt;100,則unlock,並sleep()本執行緒合適的一段時間.

這個時候,t0,t1,t2的**不變,t3的**如下

print()

else

} }

這種辦法有兩個問題

1) sum在大多數情況下不會到達100,那麼對t3的**來說,大多數情況下,走的是else分支,只是lock和unlock,然後sleep().這浪費了cpu處理時間.

2) 為了節省cpu處理時間,t3會在探測到sum沒到達100的時候sleep()一段時間.這樣卻又帶來另外乙個問題,亦即t3響應速度下降.可能在sum到達200的時候,t4才會醒過來.

3) 這樣,程式設計師在設定sleep()時間的時候陷入兩難境地,設定得太短了節省不了資源,太長了又降低響應速度.真是難辦啊!

這個時候,condition variable**外穿,從天而降,拯救了焦頭爛額的你.

你首先定義乙個condition variable.

pthread_cond_t cond_sum_ready=pthread_cond_initializer;

t0,t1,t2的**只要後面加兩行,像這樣

add()

而t3的**則是

print

注意兩點:

1) 在thread_cond_wait()之前,必須先lock相關聯的mutex, 因為假如目標條件未滿足,pthread_cond_wait()實際上會unlock該mutex, 然後block,在目標條件滿足後再重新lock該mutex, 然後返回.

2) 為什麼是while(sum<100),而不是if(sum<100) ?這是因為在pthread_cond_signal()和pthread_cond_wait()返回之間,有時間差,假設在這個時間差內,還有另外一 個執行緒t4又把sum減少到100以下了,那麼t3在pthread_cond_wait()返回之後,顯然應該再檢查一遍sum的大小.這就是用 while的用

訊號量和自旋鎖的差別

1 核心同步措施 為了避免併發,防止競爭。核心提供了一組同步方法來提供對共享資料的保護。我們的重點不是介紹這些方法的詳細用法,而是強調為什麼使用這些方法和它們之間的差別。linux 使用的同步機制可以說從2.0到2.6以來不斷發展完善。從最初的原子操作,到後來的訊號量,從大核心鎖到今天的自旋鎖。這些...

訊號量與互斥鎖

訊號量與普通整型變數的區別 訊號量 semaphore 是非負整型變數,除了初始化之外,它只能通過兩個標準原子操作 wait semap signal semap 來進行訪問 操作也被成為pv原語 p 於dutch proberen 測試 v 於 dutch verhogen 增加 而普通整型變數則...

訊號量與互斥鎖

原文 訊號量與普通整型變數的區別 訊號量 semaphore 是非負整型變數,除了初始化之外,它只能通過兩個標準原子操作 wait semap signal semap 來進行訪問 操作也被成為pv原語 p 於dutch proberen 測試 v 於 dutch verhogen 增加 而普通整型...