2009-06-16 69 views
6

調用pthread_cond_timedwait時是否有任何缺點,而沒有首先鎖定關聯的互斥鎖,並且在調用pthread_cond_signal時沒有執行互斥鎖?不鎖定pthread_cond_timedwait和pthread_cond_signal的互斥鎖(在Linux上)

在我來說真的是有沒有條件來檢查,我想非常類似於Java等待(長)行爲和notify()。

根據該文件,可以有「不可預測的調度行爲」。我不確定這意味着什麼。

一個例子程序似乎沒有先鎖定互斥體做工精細。

+0

僅僅是明確的,你要的是等待高達N秒,除非你是早醒來? – 2009-06-16 18:36:33

+0

是的。大概的信號量是一個更好的交易。 – 2009-06-16 19:26:21

回答

11

首先是不正常:

pthread_cond_timedwait()的和 pthread_cond_wait()功能須 塊上的狀態變量。他們 應互斥鎖 調用線程或未定義 行爲導致鎖被調用。

http://opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html

的原因是,實施可能要依賴於互斥被鎖定,以安全地添加到一個服務員名單。它可能希望釋放互斥鎖,而不首先檢查它是否被保留。

第二是令人不安:

如果需要可預測的調度行爲 ,則該互斥由 鎖定線程調用 pthread_cond_signal()pthread_cond_broadcast()

http://www.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_signal.html

關閉我的頭頂,我不知道具體的競爭條件是什麼,如果你沒有采取鎖定信號是弄亂調度的行爲。所以我不知道未定義的調度程序行爲可能會有多糟糕:例如,對於廣播服務器而言,只是不會按優先級順序(或者您的特定調度程序通常表現爲)獲取鎖定。或者,侍者可能會「迷路」。

儘管如此,通過條件變量您可以設置條件(至少是一個標誌)和信號,而不僅僅是信號,因此您需要採取互斥鎖。其理由是,否則,如果你同時使用另一個線程調用wait(),然後你根據等待是否()或信號()勝得到完全不同的行爲:如果信號()在第一個鑽進,那麼你會即使您關心的信號已經發生,也要等待完全超時。這很少是條件變量的用戶想要的,但對你來說可能沒問題。也許這就是「不可預知的調度程序行爲」的文檔意味着什麼 - 突然間,時間片對於程序的行爲變得至關重要。

順便說一下,在Java中,你必須有鎖,以通知()或notifyAll的():

此方法應該僅由一個 線程,它是此 對象監視器的所有者被稱爲。

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#notify()

Java的同步{/} /等待/ notifty/notifyAll的行爲類似於對pthread_mutex_lock /調用pthread_mutex_unlock /調用pthread_cond_wait /調用pthread_cond_signal /調用pthread_cond_broadcast,而不是由巧合。

+0

您可能希望修復到JavaDoc的鏈接...括號需要成爲URL的一部分。 – 2009-06-16 19:47:12

+1

如果監視器沒有被正確的線程所擁有,則拋出Java。 – Arkadiy 2009-06-16 20:39:30

+2

我認爲它的意思是,當你修改條件,然後釋放鎖,另一個線程(稱爲B)可以搶互斥,檢查車況,並認爲它是信號,並繼續該行爲。與此同時,cond_signal()正在觸發,另一個線程(稱爲A)將能夠檢查試圖鎖定互斥鎖的條件塊。當A最終確實抓住互斥鎖時,它看到B已經重置了條件,並在cond_wait中再次返回到睡眠狀態。如果所有的線程都是「相等的」,它是無害的,但理論上線程A可能會餓死。 – 2009-06-16 21:32:36

3

等待條件變量與互斥鎖配對的點是原子地進入等待並釋放鎖,即允許其他線程修改受保護狀態,然後再自動接收狀態更改的通知並獲取鎖。你所描述的可以用許多其他方法來完成,比如管道,套接字,信號,或者 - 可能是最合適的 - 信號量

1

我認爲這應該工作(注意,未經測試的代碼):

// initialize a semaphore 
sem_t sem; 
sem_init(&sem, 
    0, // not shared 
    0 // initial value of 0 
    ); 


// thread A 
struct timespec tm; 
struct timeb tp; 

const long sec  = msecs/1000; 
const long millisec = msecs % 1000; 

ftime(&tp); 
tp.time += sec; 
tp.millitm += millisec; 
if(tp.millitm > 999) { 
    tp.millitm -= 1000; 
    tp.time++; 
} 
tm.tv_sec = tp.time; 
tm.tv_nsec = tp.millitm * 1000000; 

// wait until timeout or woken up 
errno = 0; 
while((sem_timedwait(&sem, &tm)) == -1 && errno == EINTR) { 
    continue; 
} 

return errno == ETIMEDOUT; // returns true if a timeout occured 


// thread B 
sem_post(&sem); // wake up Thread A early 
-1

「不可預測的調度行爲」 是指這一點。你不知道會發生什麼。 也沒有執行。它可以按預期工作。它可能會導致應用程序崩潰它可以很好地工作多年,然後競爭條件使你的應用程序變成猴子。它可能會陷入僵局。

基本上,如果任何文檔顯示任何未定義/不可預知的事情會發生,除非您做文檔告訴您要做的事情,那麼您最好這樣做。其他的東西可能會炸燬你的臉。 (它不會炸燬,直到你把代碼投入生產,只是爲了更多地激怒你)至少這是我的經驗

9

Butenhof出色的「使用POSIX線程編程」在第3.3章0.3。

基本上,信令condvar不鎖定的互斥爲潛在性能優化:如果信令螺紋具有鎖定互斥,那麼線程醒來上condvar必須立即互斥阻止信令線程已鎖定即使信號線程沒有修改等待線程將使用的任何數據。

提到「不可預知調度程序行爲」的原因是,如果您有一個等待condvar的高優先級線程(另一個線程將發信號並喚醒高優先級線程),則任何其他較低優先級的線程可以來鎖定互斥鎖,以便在發出condvar信號並且高優先級線程被喚醒時,它必須等待較低優先級的線程釋放互斥鎖。如果在信號發送時互斥鎖被鎖定,那麼優先級較高的線程將在較低優先級的線程之前在互斥體上進行調度:基本上,您知道當您喚醒高優先級的線程時,只要調度程序允許它(當然,在發出高優先級線程信號之前,您可能需要等待互斥體,但這是另一個問題)。

0

只要有可能,應在互斥體外發出信號。互斥編程是併發編程中的必要罪惡。它們的使用導致競爭,從而使系統獲得使用多個處理器所能獲得的最大性能。

互斥體的目的是防止訪問程序中的某些共享變量,以便它們以原子方式運行。當信號操作在互斥體內完成時,會導致將數百個不相關的機器週期包含到互斥體中,這與保護共享數據無關。它有可能從用戶空間一直調用到內核中。

有關標準的「可預測的調度行爲」的註釋是完全僞造的。

當我們希望機器以可預測的,明確定義的順序執行語句時,其工具就是單個執行線程中的語句的排序:S1 ; S2。聲明S1S2之前爲「預定」。

我們使用線程時,我們意識到,一些動作是獨立的,它們的調度順序並不重要,並且有得以實現,喜歡在多個處理器上的實時事件或計算響應更及時的性能優勢。

在調度順序確實在多個線程中變得重要時,這屬於優先級的概念。優先級解決了當N個語句中的任何一個可能被調度執行時首先發生的事情。多線程下訂購的另一個工具是排隊。事件由一個或多個線程放入隊列中,單個服務線程按隊列順序處理事件。

底線是,的pthread_cond_broadcast的位置並不用於控制執行順序的適當的工具。它不會使執行順序可預測,因爲程序突然在每個平臺上具有完全相同的可重複行爲。