2013-04-08 65 views
13

我需要同步std::condition_variable/condition_variable_any::notify_one我需要同步的std :: condition_variable/condition_variable_any :: notify_one

至於我可以看到,如果丟失的通知是可以接受的 - 這是確定調用notify_one不受保護(mutex所爲例)。

舉例來說,我看到了下面的使用模式(抱歉,不記得在哪裏):

{ 
    { 
     lock_guard<mutex> l(m); 
     // do work 
    } 
    c.notify_one(); 
} 

但是,我考察的libstdC++源代碼,我看到:

condition_variable::notify_one

void condition_variable::notify_one() noexcept 
{ 
    int __e = __gthread_cond_signal(&_M_cond); 
    // XXX not in spec 
    // EINVAL 
    if (__e) 
     __throw_system_error(__e); 
} 

and condition_variable_any::notify_one

void condition_variable_any::notify_one() noexcept 
{ 
    lock_guard<mutex> __lock(_M_mutex); 
    _M_cond.notify_one(); 
} 

這裏是condition_variable_any的佈局:

class condition_variable_any 
{ 
    condition_variable _M_cond; 
    mutex _M_mutex; 
    // data end 

即它只是在condition_variable + mutex周圍的薄包裝。

所以,問題:

  1. 它是線程安全的,不會被用於互斥或者condition_variable_anycondition_variable保護notify_one
  2. 爲什麼condition_variable_any的實現使用額外的互斥量?
  3. 爲什麼實施condition_variable_any::notify_onecondition_variable::notify_one有所不同?可能condition_variable::notify_one需要手動保護,但condition_variable_any::notify_one不需要?它是libstdC++的錯誤?

回答

13

I.e.它只是在condition_variable + mutex周圍的薄包裝。

呃,沒有。僅僅因爲它具有這些類型的成員並不能使它成爲一個簡單的包裝器。嘗試瞭解它的實際作用,而不僅僅是其私人成員的類型。這裏有一些非常微妙的代碼。

  1. 它是線程安全的,不會被用於互斥或者condition_variable_any或condition_variable保護notify_one?

是。

事實上,調用notify_one()鎖定互斥鎖將導致等待線程喚醒,嘗試鎖定互斥鎖,發現它仍然被通知線程鎖定,並返回休眠狀態直到釋放互斥鎖。

如果您在沒有鎖定互斥鎖的情況下調用notify_one(),則可以立即運行喚醒線程。

2爲什麼condition_variable_any的實現使用額外的互斥量?

condition_variable_any可以與任何可鎖定類型可以使用,而不僅僅是std:mutex,但內部的一箇中的libstdC++使用一個condition_variable,其只能與std::mutex被使用,所以它具有一個內部std::mutex對象太。

因此,condition_variable_any與兩個互斥體一起工作,外部互斥體由用戶提供,而內部的互斥體由實現使用。

3爲什麼condition_variable_any :: notify_one和condition_variable :: notify_one的實現有所不同?也許condition_variable :: notify_one需要手動保護,但condition_variable_any :: notify_one不?它是libstdC++的錯誤?

不,這不是一個錯誤。

該標準要求呼叫wait(mx)必須原子解鎖mx和睡眠。 libstdC++使用內部互斥來保證原子性。如果其他線程正在等待condition_variable_any,則必須鎖定內部互斥鎖以避免錯過通知。

+0

謝謝!這幫助了我很多。特別回答2和3 - 關於原子性的好處,以及無論如何都使用互斥體的內部元素。順便說一句,您可以添加指向pthread_cond_wait http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_cond_wait.html的鏈接,以顯示最常見的實現僅適用於內部中它自己的互斥體。你能否澄清一下你的意思是「這裏有一些非常微妙的代碼」。 – qble 2013-04-09 20:31:33

+0

關於1. - 可否鎖定condition_variable :: notify_one導致錯過通知?即線程#1在互斥體中執行任務,發送結果,解鎖互斥體,[同時]線程#2鎖定互斥體但尚未調用wait,[同時]線程#1調用notify_one,[同時]線程#2調用wait - 通知丟失。 – qble 2013-04-09 20:37:04

+0

這不是一個丟失的通知,這是等待線程在等待之前不檢查條件謂詞。鎖定互斥鎖並不能解決這種情況,通知仍可能在等待線程鎖定互斥鎖之前發生。您**必須在等待條件變量時檢查關聯的謂詞 – 2013-04-10 00:27:24

2

(1)從數據競賽的角度看,我沒有看到任何信號條件變量的原因必須由互斥鎖來保護。很明顯,您有可能收到多餘的通知或丟失通知,但如果這是您的程序的可接受或可恢復的錯誤條件,我不相信標準中會有任何內容會導致其不合法。當然,這個標準不會防止你對抗競爭條件;程序員有責任確保競賽條件良好。 (當然,程序員不應該放置任何「數據競賽」,這些數據競賽在標準中有非常明確的定義,但不直接適用於同步原語,或者未定義的行爲被召喚。)

( 2)我無法回答這樣的關於標準庫設施的內部實現的問題。當然,供應商有責任提供正確工作並符合規格的圖書館設施。這個庫的實現可能有一些內部狀態,需要互斥以避免損壞,或者它可能會執行鎖定以避免丟失或重複的通知。 (只是因爲你的程序可以容忍它們,並不意味着圖書館的任意用戶可以,而且一般情況下我希望它們不能)。我只是猜測他們在用這個互斥體保護什麼。

(3)condition_variable_any適用於任何類似鎖的物體,而condition_variable專門設計用於unique_lock<mutex>。後者可能比前者更容易實現和/或更高性能,因爲它明確地知道它正在操作哪種類型以及它們需要什麼(它們是否微不足道,它們是否適合高速緩存行,它們是否直接映射到特定平臺的一系列系統調用,它們所暗含的保護範圍或緩存一致性等),而前者提供了用於在鎖定對象上操作的通用工具,而不會受到std::mutexstd::unique_lock<>的約束限制。

+0

也許我應該添加到Q:condition_variable_any的實現僅僅是condition_variable + mutex的薄包裝。 – qble 2013-04-08 20:35:04

+0

「只是因爲你的程序可以容忍他們,並不意味着圖書館的任意用戶可以,而且一般情況下,我希望他們不能」 - 我認爲你不明白我的觀點 - 如果我不保護我的notify_one通過我自己的互斥 - 可能會丟失通知,但如果我保留它 - 它們應該是不可能的。當用戶不通過互斥保護notify_one時,實現不應該關心丟失的通知。 – qble 2013-04-08 20:38:32

+0

@qble:我再次無法評論標準庫供應商選擇如何實現標準的實現。他們可以使用任何編譯器的魔法來使其運行(包括未定義的行爲,如果他們碰巧知道他們的平臺定義了它),並且他們可以利用非常具體的知識。這可能是因爲他們知道std :: lock_guard 對他們來說足夠原子化,但與模板參數關聯的類型可能不會。 您可能會在執行wait()時發現一條線索。 – 2013-04-08 22:13:45