注意信號量與條件變量的區別
信號量內容可見:http://www.cnblogs.com/charlesblc/p/6142868.html
信號量、共享內存,以及消息隊列等System V IPC三劍客主要關注
進程間通信;
而條件變量、互斥鎖,主要關注
線程間通信。
pthread_cond_wait指的是
條件變量,總和一個
互斥鎖結合使用。在
調用pthread_cond_wait前要先獲取鎖。pthread_cond_wait函數執行時先
自動釋放指定的鎖,然后等待條件變量的變化。在函數調用返回之前,自動將指定的互斥量
重新鎖住。
int pthread_cond_signal(pthread_cond_t * cond);
pthread_cond_signal通過條件變量cond發送消息,若多個消息在等待,它只喚醒一個。
pthread_cond_broadcast可以喚醒所有。調用pthread_cond_signal后要
立刻釋放互斥鎖,因為pthread_cond_wait的最后一步是要將指定的互斥量重新鎖住,如果pthread_cond_signal之后沒有釋放互斥鎖,pthread_cond_wait仍然要阻塞。
無論哪種等待方式,都必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的競爭條件(Race Condition)。mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應鎖 (PTHREAD_MUTEX_ADAPTIVE_NP)
互斥鎖的類型,有以下幾種:
PTHREAD_MUTEX_TIMED_NP,這是缺省值,也就是普通鎖。當一個線程加鎖以后,其余請求鎖的線程將形成一個等待隊列,並在解鎖后按優先級獲得鎖。這種鎖策略保證了資源分配的公平性。
PTHREAD_MUTEX_RECURSIVE_NP,嵌套鎖,允許同一個線程對同一個鎖成功獲得多次,並通過多次unlock解鎖。如果是不同線程請求,則在加鎖線程解鎖時重新競爭。
PTHREAD_MUTEX_ERRORCHECK_NP,檢錯鎖,如果同一個線程請求同一個鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動作相同。這樣就保證當不允許多次加鎖時不會出現最簡單情況下的死鎖。
PTHREAD_MUTEX_ADAPTIVE_NP,適應鎖,動作最簡單的鎖類型,僅等待解鎖后重新競爭。
且在調用pthread_cond_wait()前必須由本線程加鎖 (pthread_mutex_lock()),而在更新條件等待隊列以前,mutex保持鎖定狀態,並在線程掛起進入等待前解鎖。在條件滿足從而離開 pthread_cond_wait()之前,mutex將被重新加鎖,以與進入pthread_cond_wait()前的加鎖動作對應。
激發條件有兩種形式,pthread_cond_signal()激活一個等待該條件的線程,存在多個等待線程時按入隊順序激活其中一個;而pthread_cond_broadcast()則激活所有等待線程。
下面是另一處說明:給出了函數運行全過程。 為什么在喚醒線程后要重新mutex加鎖?
了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 線程信號發送系統的核心,也是最難以理解的部分。
首先,讓我們考慮以下情況:線程為查看已鏈接列表而鎖定了互斥對象,然而該列表恰巧是空的。這一特定線程什么也干不了 -- 其設計意圖是從列表中除去節點,但是現在卻沒有節點。因此,它只能:
鎖定互斥對象時,線程將調用 pthread_cond_wait(&mycond,&mymutex)。
鎖定互斥對象時,線程將調用 pthread_cond_wait(&mycond,&mymutex)。
pthread_cond_wait() 所做的
第一件事就是同時對互斥對象解鎖(於是其它線程可以修改已鏈接列表),並等待條件 mycond 發生(這樣當 pthread_cond_wait() 接收到另一個線程的“信號”時,它將蘇醒)。現在互斥對象已被解鎖,其它線程可以訪問和修改已鏈接列表,可能還會添加項。 【
要求解鎖並阻塞是一個原子操作】
此時,pthread_cond_wait() 調用還未返回。對互斥對象解鎖會立即發生,但等待條件 mycond 通常是一個阻塞操作,這意味着線程將睡眠,在它蘇醒之前不會消耗 CPU 周期。這正是我們期待發生的情況。線程將
一直睡眠,直到特定條件發生,在這期間不會發生任何浪費 CPU 時間的繁忙查詢。從線程的角度來看,它只是在等待 pthread_cond_wait() 調用返回。
現在繼續說明,假設另一個線程(稱作“2 號線程”)鎖定了 mymutex 並對已鏈接列表添加了一項。在對互斥對象解鎖之后,2 號線程會立即調用函數 pthread_cond_broadcast(&mycond)。此操作之后,2 號線程將使所有等待 mycond 條件變量的線程立即蘇醒。這意味着第一個線程(仍處於 pthread_cond_wait() 調用中)現在將
蘇醒。
是先解鎖還是先signal,各有優缺點,下文會分析。
另外注意,signal的函數里面是不是解鎖加鎖的,跟wait不一樣。
現在,看一下第一個線程發生了什么。您可能會認為在 2 號線程調用 pthread_cond_broadcast(&mymutex) 之后,1 號線程的 pthread_cond_wait() 會立即返回。不是那樣!實際上,pthread_cond_wait() 將執行最后一個操作:
重新鎖定 mymutex。一旦 pthread_cond_wait() 鎖定了互斥對象,那么它將返回並允許 1 號線程繼續執行。那時,它可以馬上檢查列表,查看它所感興趣的更改。
實際上,一般是先解鎖。
來看一個例子:
In Thread1: pthread_mutex_lock(&m_mutex); pthread_cond_wait(&m_cond,&m_mutex); pthread_mutex_unlock(&m_mutex); In Thread2: pthread_mutex_lock(&m_mutex); pthread_cond_signal(&m_cond); pthread_mutex_unlock(&m_mutex);
為什么要與pthread_mutex 一起使用呢?
這是為了應對
線程1在調用pthread_cond_wait()但線程1還沒有進入wait cond的狀態的時候,此時線程2調用了 cond_singal 的情況。 如果不用mutex鎖的話,這個cond_singal就丟失了。加了鎖的情況是,線程2必須等到 mutex 被釋放(也就是 pthread_cod_wait()
釋放鎖並進入wait_cond狀態 ,此時線程2上鎖) 的時候才能調用cond_singal.
pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之間,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是各有有缺點。
之間:
pthread_mutex_lock
xxxxxxx
pthread_cond_signal
pthread_mutex_unlock
pthread_mutex_lock
xxxxxxx
pthread_cond_signal
pthread_mutex_unlock
缺點:在某下線程的實現中,會造成等待線程從內核中喚醒(由於cond_signal)然后又回到內核空間(因為
cond_wait返回后會有原子加鎖的 行為)(注:意思是說這時候signal的線程還沒有unlock,所以wait的線程加鎖會導致堵塞,並進入內核),所以一來一回會有性能的問題。但是在LinuxThreads或者NPTL里面,就不會有這個問題,因為在Linux 線程中,有兩個隊列,分別是
cond_wait隊列和mutex_lock隊列, cond_signal只是讓線程從cond_wait隊列移到mutex_lock隊列,而不用返回到用戶空間,不會有性能的損耗。
所以在Linux中推薦使用這種模式。
所以在Linux中推薦使用這種模式。
之后:
pthread_mutex_lock
xxxxxxx
pthread_mutex_unlock
pthread_cond_signal
優點:不會出現之前說的那個潛在的性能損耗,因為在signal之前就已經釋放鎖了
缺點:如果unlock和signal之前,有個 低優先級的線程正在mutex上等待的話,那么這個低優先級的線程就會搶占高優先級的線程(cond_wait的線程),而這在上面的放中間的模式下是不會出現的。
pthread_mutex_lock
xxxxxxx
pthread_mutex_unlock
pthread_cond_signal
優點:不會出現之前說的那個潛在的性能損耗,因為在signal之前就已經釋放鎖了
缺點:如果unlock和signal之前,有個 低優先級的線程正在mutex上等待的話,那么這個低優先級的線程就會搶占高優先級的線程(cond_wait的線程),而這在上面的放中間的模式下是不會出現的。