2017-01-26 93 views
1

Accoding到cppreference.com之前修改共享的「原子」變量:爲什麼我需要獲得鎖的通知condition_variable

是打算修改變量有

  1. 獲得線程std :: mutex(通常通過std :: lock_guard)
  2. 執行修改,同時鎖定被鎖定
  3. 對std :: condition_variable執行notify_one或notify_all(鎖定不需要舉行通知)

即使共享變量是原子的,它必須在互斥量下修改才能正確地將修改發佈到等待線程。

我不太明白,爲什麼修改一個原子變量需要一個鎖。請看下面的代碼片段:

static std::atomic_bool s_run {true}; 
static std::atomic_bool s_hasEvent {false}; 
static std::mutex s_mtx; 
static std::condition_variabel s_cv; 


// Thread A - the consumer thread 
function threadA() 
{ 
    while (s_run) 
    { 
     { 
      std::unique_lock<std::mutex> lock(s_mtx); 
      s_cv.wait(lock, [this]{ 
       return m_hasEvents.load(std::memory_order_relaxed); 
      }); 
     } 

     // process event 
     event = lockfree_queue.pop(); 
     ..... code to process the event .... 
    } 
} 


// Thread B - publisher thread 
function PushEvent(event) 
{ 
    lockfree_queque.push(event) 
    s_hasEvent.store(true, std::memory_order_release); 
    s_cv.notify_one(); 
} 

在PushEvent功能,我不掌握s_mtx因爲s_hasEvent是一個原子變量,隊列lockfree。沒有獲取s_mtx鎖定的問題是什麼?

+0

條件變量是共享狀態,因此必須由互斥鎖保護。 –

+0

問題是關於通過互斥體(也是共享狀態)而不是condvar保護's_hasEvents'。發佈者不會修改condvar,它只會調用'notify_one()',這在互斥鎖被鎖定時不需要完成。 –

回答

1

找到關於這個問題的很好的解釋in another thread。拿起戰利品

問題以下詢問有關競賽條件。

如果被傳遞的數據是原子的,那麼我們不能在「發送」方沒有互斥體嗎?

最後。

2

正如Yakk's answer to the question you linked to指出這是爲了防止這樣的事件序列導致錯過喚醒:

    1. 線程A鎖定互斥。
  • 線程A調用拉姆達的閉合件做m_hasEvents.load(std::memory_order_relaxed);並返回值false
    1. 線程A被調度程序中斷,線程B開始運行。
  • 線程B推事件到隊列中並存儲到s_hasEvent
  • 線程B運行s_cv.notify_one()
    1. 線程B被調度程序中斷,線程A再次運行。
  • 線程A評估由閉合返回的false結果,決定沒有未決事件。
    1. 線程A條件變量上的塊等待事件。

這意味着notify_one()呼叫已經錯過了,條件變量將阻止即使沒有準備在隊列中的事件。

如果在鎖定互斥鎖時完成對共享變量的更新,則步驟4不可能在步驟2和7之間發生,因此條件變量對事件的檢查會得到一致的結果。由於發佈者和消費者使用的互斥鎖,或者到s_hasEvent的商店發生在步驟1之前(並且關閉加載值true並且從不阻止條件變量),或者它發生在步驟8之後(並且因此notify_one()調用將被喚醒它)。

相關問題