導航:首頁 > 網路營銷 > semtrywait

semtrywait

發布時間:2020-10-31 05:27:57

1、sem_trywait的內容簡介

<

2、sem_t的初始化信號量

它的原型為: extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
頭文件為: #include <semaphore.h>
sem為指向信號量結構的一個指針;
pshared不為0時此信號量在進程間共享,否則只能為當前進程的所有線程共享;
value給出了信號量的初始值。
函數sem_post( sem_t *sem )用來增加信號量的值當有線程阻塞在這個信號量上時,調用這個函數會使其中的一個線程不再阻塞,選擇機制同樣是由線程的調度策略決定的。
函數sem_wait( sem_t *sem )被用來阻塞當前線程直到信號量sem的值大於0,解除阻塞後將sem的值減一,表明公共資源經使用後減少。
函數sem_trywait ( sem_t *sem )是函數sem_wait()的非阻塞版本,它直接將信號量sem的值減一。
函數sem_destroy(sem_t *sem)用來釋放信號量sem。

3、在Quartez中操作任務類設置阻塞時間,讓下個線程延遲,用sleep(5000)。但是下一個線程還是按時觸發了

援引CU上一篇帖子的內容:
「信號量用在多線程多任務同步的,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作(大家都在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()
{
pthread_mutex_lock(lock_s);
sum++;
pthread_mutex_unlock(lock_s);
}
如果操作比較復雜,假設線程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()
{
while (1)
{
pthread_mutex_lock(lock_s);
if(sum<100)
{
printf(「sum reach 100!」);
pthread_mutex_unlock(lock_s);
}
else
{
pthread_mutex_unlock(lock_s);
my_thread_sleep(100);
return OK;
}
}
}
這種辦法有兩個問題
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()
{
pthread_mutex_lock(lock_s);
sum++;
pthread_mutex_unlock(lock_s);
if(sum>=100)
pthread_cond_signal(&cond_sum_ready);
}
而t3的代碼則是
print
{
pthread_mutex_lock(lock_s);
while(sum<100)
pthread_cond_wait(&cond_sum_ready, &lock_s);
printf(「sum is over 100!」);
sum=0;
pthread_mutex_unlock(lock_s);
return OK;
}
注意兩點:
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的用意

4、線程同步:何時互斥鎖不夠,還需要條件變數

信號量強調的是線程(或進程)間的同步:「信號量用在多線程多任務同步的,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作(大家都 在sem_wait的時候,就阻塞在那裡)。當信號量為單值信號量是,也可以完成一個資源的互斥訪問。


有名信號量:可以用於不同進程間或多線程間的互斥與同步

創建打開有名信號量

sem_t *sem_open(const char *name, int oflag);

sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);


成功返回信號量指針;失敗返回SEM_FAILED,設置errnoname是文件路徑名,但不能寫成/tmp/a.sem這樣的形式,因為在linux下,sem都是在/dev/shm目錄下,可寫成"/mysem"或"mysem",創建出來的文件都 是"/dev/shm/sem.mysem",mode設置為0666,value設置為信號量的初始值.所需信號燈等已存在條件下指定O_CREAT|O_EXCL卻是個錯誤。


關閉信號量,進程終止時,會自動調用它

int sem_close(sem_t *sem);

成功返回0;失敗返回-1,設置errno


刪除信號量,立即刪除信號量名字,當其他進程都關閉它時,銷毀它

int sem_unlink(const char *name);


等待信號量,測試信號量的值,如果其值小於或等於0,那麼就等待(阻塞);一旦其值變為大於0就將它減1,並返回

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);


成功返回0;失敗返回-1,設置errno


當信號量的值為0時,sem_trywait立即返回,設置errno為EAGAIN。如果被某個信號中斷,sem_wait會過早地返回,設置errno為EINTR


發出信號量,給它的值加1,然後喚醒正在等待該信號量的進程或線程

int sem_post(sem_t *sem);


成功返回0;失敗返回-1,不會改變它的值,設置errno,該函數是非同步信號安全的,可以在信號處理程序里調用它無名信號量,用於進程體內各線程間的互斥和同步,使用如下API(無名信號量,基於內存的信號量)


(1)、sem_init


功能:用於創建一個信號量,並初始化信號量的值。


頭文件:


函數原型: int sem_init (sem_t* sem, int pshared, unsigned int value);


函數傳入值: sem:信號量。pshared:決定信號量能否在幾個進程間共享。由於目前LINUX還沒有實現進程間共享信息量,所以這個值只能取0。


(2)其他函數。

int sem_wait (sem_t* sem);

int sem_trywait (sem_t* sem);

int sem_post (sem_t* sem);

int sem_getvalue (sem_t* sem);

int sem_destroy (sem_t* sem);


功能:sem_wait和sem_trywait相當於P操作,它們都能將信號量的值減一,兩者的區別在於若信號量的值小於零時,sem_wait將會阻塞進程,而sem_trywait則會立即返回。sem_post相當於V操作,它將信號量的值加一,同時發出喚醒的信號給等待的進程(或線程)。


sem_getvalue 得到信號量的值。


sem_destroy 摧毀信號量。


如果某個基於內存的信號燈是在不同進程間同步的,該信號燈必須存放在共享內存區中,這要只要該共享內存區存在,該信號燈就存在。


互斥鎖(又名互斥量)強調的是資源的訪問互斥:互斥鎖是用在多線程多任務互斥的,一個線程佔用了某一個資源,那麼別的線程就無法訪問,直到這個線程unlock,其他的線程才開始可以利用這個資源。比如對全局變數的訪問,有時要加鎖,操作完了,在解鎖。有的時候鎖和信號量會同時使用的」


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


在linux下, 線程的互斥量數據類型是pthread_mutex_t. 在使用前, 要對它進行初始化:


對於靜態分配的互斥量, 可以把它設置為PTHREAD_MUTEX_INITIALIZER, 或者調用pthread_mutex_init.


對於動態分配的互斥量, 在申請內存(malloc)之後, 通過pthread_mutex_init進行初始化, 並且在釋放內存(free)前需要調用pthread_mutex_destroy.

原型:

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric attr);

int pthread_mutex_destroy(pthread_mutex_t *mutex);


頭文件:


返回值: 成功則返回0, 出錯則返回錯誤編號.


說明: 如果使用默認的屬性初始化互斥量, 只需把attr設為NULL. 其他值在以後講解.


首先說一下加鎖函數:


頭文件:

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);


返回值: 成功則返回0, 出錯則返回錯誤編號.


說 明: 具體說一下trylock函數, 這個函數是非阻塞調用模式, 也就是說, 如果互斥量沒被鎖住, trylock函數將把互斥量加鎖, 並獲得對共享資源的訪問許可權; 如果互斥量 被鎖住了, trylock函數將不會阻塞等待而直接返回EBUSY, 表示共享資源處於忙狀態.


再說一下解所函數:


頭文件:

原型: int pthread_mutex_unlock(pthread_mutex_t *mutex);


返回值: 成功則返回0, 出錯則返回錯誤編號.


條件變數常與互斥鎖同時使用,達到線程同步的目的:條件變數通過允許線程阻塞和等待另一個線程發送信號的方法彌補了互斥鎖的不足。在發 送信號時,如果沒有線程 等待在該條件變數上,那麼信號將丟失;而信號量有計數值,每次信號量post操作都會被記錄


互斥鎖必須是誰上鎖就由誰來解鎖,而信號量的wait和post操作不必由同一個線程執行。

2. 互斥鎖要麼被鎖住,要麼被解開,和二值信號量類似


3. sem_post是各種同步技巧中,唯一一個能在信號處理程序中安全調用的函數


4. 互斥鎖是為上鎖而優化的;條件變數是為等待而優化的; 信號量既可用於上鎖,也可用於等待,因此會有更多的開銷和更高的復雜性


5. 互斥鎖,條件變數都只用於同一個進程的各線程間,而信號量(有名信號量)可用於不同進程間的同步。當信號量用於進程間同步時,要求信號量建立在共享內存區。


6. 信號量有計數值,每次信號量post操作都會被記錄,而條件變數在發送信號時,如果沒有線程在等待該條件變數,那麼信號將丟失。


讀寫鎖


讀寫鎖與互斥量類似,不過讀寫鎖允許更高的並行性。互斥量要麼是鎖住狀態要麼是不加鎖狀態,而且一次只有一個線程可以對其加鎖。


讀寫鎖可以由三種狀態:讀模式下加鎖狀態、寫模式下加鎖狀態、不加鎖狀態。一次只有一個線程可以佔有寫模式的讀寫鎖,但是多個線程可以同時佔有讀模式的讀寫

鎖。


在讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的線程都會被阻塞。當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的線程都可以得到訪問權,但是如果線程希望以寫模式對此鎖進行加鎖,它必須阻塞直到所有的線程釋放讀鎖。雖然讀寫鎖的實現各不相同,但當讀寫鎖處於讀模式鎖住狀態時,如果有另外的線程試圖以寫模式加鎖,讀寫鎖通常會阻塞隨後的讀模式鎖請求。這樣可以避免讀模式鎖長期佔用,而等待的寫模式鎖請求一直得不到滿足。


讀寫鎖非常適合於對數據結構讀的次數遠大於寫的情況。當讀寫鎖在寫模式下時,它所保護的數據結構就可以被安全地修改,因為當前只有一個線程可以在寫模式下擁 有這個鎖。當讀寫鎖在讀狀態下時,只要線程獲取了讀模式下的讀寫鎖,該鎖所保護的數據結構可以被多個獲得讀模式鎖的線程讀取。


讀寫鎖也叫做共享-獨占鎖,當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的;當他以寫模式鎖住時,它是以獨占模式鎖住的。

初始化和銷毀:

#include

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);


成功則返回0, 出錯則返回錯誤編號.


同互斥量以上, 在釋放讀寫鎖佔用的內存之前, 需要先通過thread_rwlock_destroy對讀寫鎖進行清理工作, 釋放由init分配的資源.


讀和寫:

#include

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);


成功則返回0, 出錯則返回錯誤編號.


這3個函數分別實現獲取讀鎖, 獲取寫鎖和釋放鎖的操作. 獲取鎖的兩個函數是阻塞操作, 同樣, 非阻塞的函數為:

#include

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);


成功則返回0, 出錯則返回錯誤編號.


非阻塞的獲取鎖操作, 如果可以獲取則返回0, 否則返回錯誤的EBUSY.


雖然讀寫鎖提高了並行性,但是就速度而言並不比互斥量快.


可能這也是即使有讀寫鎖存在還會使用互斥量的原因,因為他在速度方面略勝一籌。這就需要我們在寫程序的時候綜合考慮速度和並行性並找到一個折中。


比如: 假設使用互斥量需要0.5秒,使用讀寫鎖需要0.8秒。在類似學生管理系統這類軟體中,可能百分之九十的時間都是查詢操作,那麼假如現在突然來個個20個請求,如果使用的是互斥量,那麼最後的那個查詢請求被滿足需要10後。這樣,估計沒人能受得了。而使用讀寫鎖,應為 讀鎖能夠多次獲得。所以所有的20個請求,每個請求都能在1秒左右得到滿足。


也就是說,在一些寫操作比較多或是本身需要同步的地方並不多的程序中我們應該使用互斥量,而在讀操作遠大於寫操作的一些程序中我們應該使用讀寫鎖來進行同步


條件變數(condition)


條件變數與互斥量一起使用時,允許線程以無競爭的方式等待特定的條件發生。


條件本身是由互斥量保護的。線程在改變條件狀態前必須首先鎖住互斥量,其它線程在獲得互斥量之前不會察覺到這種改變,因此必須鎖定互斥量以後才能計算條件。


條件的檢測是在互斥鎖的保護下進行的。如果一個條件為假,一個線程自動阻塞,並釋放等待狀態改變的互斥鎖。如果另一個線程改變了條件,它發信號給關聯的條件


變數,喚醒一個或多個等待它的線程,重新獲得互斥鎖,重新評價條件。如果兩進程共享可讀寫的內存,條件變數可以被用來實現這兩進程間的線程同步。


初始化:

條件變數採用的數據類型是pthread_cond_t, 在使用之前必須要進行初始化, 這包括兩種方式:


靜態: 可以把常量PTHREAD_COND_INITIALIZER給靜態分配的條件變數.

動態: pthread_cond_init函數, 是釋放動態條件變數的內存空間之前, 要用pthread_cond_destroy對其進行清理.

#include

int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);

int pthread_cond_destroy(pthread_cond_t *cond);


成功則返回0, 出錯則返回錯誤編號.


注意:條件變數佔用的空間並未被釋放。


當pthread_cond_init的attr參數為NULL時, 會創建一個默認屬性的條件變數; 非默認情況以後討論.


2. 等待條件:

#include

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);


成功則返回0, 出錯則返回錯誤編號.


這兩個函數分別是阻塞等待和超時等待.


等待條件函數等待條件變為真, 傳遞給pthread_cond_wait的互斥量對條件進行保護, 調用者把鎖住的互斥量傳遞給函數. 函數把調用線程放到等待條件的線程列表上, 然後對互斥量解鎖, 這兩個操作是原子的. 這樣 便關閉了條件檢查和線程進入休眠狀態等待條件改變這兩個操作之間的時間通道, 這樣線程就不會錯過條件的任何變化.


當pthread_cond_wait返回時, 互斥量再次被鎖住.


pthread_cond_wait函數的返回並不意味著條件的值一定發生了變化,必須重新檢查條件的值。


pthread_cond_wait函數返回時,相應的互斥鎖將被當前線程鎖定,即使是函數出錯返回。


阻塞在條件變數上的線程被喚醒以後,直到pthread_cond_wait()函數返回之前條件的值都有可能發生變化。所以函數返回以後,在鎖定相應的互斥鎖之前,必須重新測試條 件值。最好的測試方法是循環調用pthread_cond_wait函數,並把滿足條件的表達式置為循環的終止條件。如:


pthread_mutex_lock();


while (condition_is_false)


pthread_cond_wait();


pthread_mutex_unlock();


阻塞在同一個條件變數上的不同線程被釋放的次序是不一定的。


注意:pthread_cond_wait()函數是退出點,如果在調用這個函數時,已有一個掛起的退出請求,且線程允許退出,這個線程將被終止並開始執行善後處理函數,而這時和條 件變數相關的互斥鎖仍將處在鎖定狀態。


pthread_cond_timedwait函數到了一定的時間,即使條件未發生也會解除阻塞。這個時間由參數abstime指定。函數返回時,相應的互斥鎖往往是鎖定的,即使是函數出錯返回。


注意:pthread_cond_timedwait函數也是退出點。


超時時間參數是指一天中的某個時刻。使用舉例:


pthread_timestruc_t to;


to.tv_sec = time(NULL) + TIMEOUT;


to.tv_nsec = 0;


超時返回的錯誤碼是ETIMEDOUT。


3. 通知條件:

#include

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);


成功則返回0, 出錯則返回錯誤編號.


這兩個函數用於通知線程條件已經滿足. 調用這兩個函數, 也稱向線程或條件發送信號. 必須注意, 一定要在改變條件狀態以後再給線程發送信號.

5、linux下,用gcc編譯c代碼,error:undefined reference to sem_wait 怎麼解決?謝謝! 請問 在哪兒找到的

#include <semaphore.h>

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

Link with -lrt or -pthread.

自己看最後一句....手冊裡面寫著呢....link with -lrt or -pthread

與semtrywait相關的知識