2017-05-09 29 views
0

我想了解condition_variables。線程鎖定互斥快於std :: conditional_variable :: wait()

我想我的代碼應該像:
1.主鎖MX
2.主要的wait()通知< =這裏鎖定解除
3.螺紋鎖MX
4線程發送通知
5線程解鎖mx
6.主等待()完成並鎖定mx

那麼爲什麼線程可以在通知後鎖定mx比wait()更快?
Example

#include <iostream> 
#include <future> 
#include <condition_variable> 
#include <vector> 

using namespace std::chrono_literals; 

std::shared_future<void> ready; 

std::mutex finish_mx; 
std::condition_variable finish_cv; 

int execute(int val, const std::shared_future<void> &ready){ 
    ready.wait(); 

    std::lock_guard<std::mutex> lock(finish_mx); 
    std::cout<<"Locked: "<<val<<std::endl; 
    finish_cv.notify_one(); 

    return val; 
} 


int main() 
{ 
    std::promise<void> promise; 
    auto shared = promise.get_future().share(); 

    std::vector<std::future<int>> pool; 
    for (int i=0; i<10; ++i){ 
     auto fut = std::async(std::launch::async, execute, i, std::cref(shared)); 
     pool.push_back(std::move(fut)); 
    } 

    std::this_thread::sleep_for(100ms); 

    std::unique_lock<std::mutex> finish_lock(finish_mx); 
    promise.set_value(); 

    for (int i=0; pool.size() > 0; ++i) 
    { 
     finish_cv.wait(finish_lock); 
     std::cout<<"Notifies: "<<i<<std::endl; 

     for (auto it = pool.begin(); it != pool.end(); ++it) { 
      auto state = it->wait_for(0ms); 
      if (state == std::future_status::ready) { 
       pool.erase(it); 
       break; 
      } 
     } 
    } 
} 

輸出示例:

Locked: 6 
Locked: 7 
Locked: 8 
Locked: 9 
Locked: 5 
Locked: 4 
Locked: 3 
Locked: 2 
Locked: 1 
Notifies: 0 
Locked: 0 
Notifies: 1 

編輯

for (int i=0; pool.size() > 0; ++i) 
{ 
    finish_cv.wait(finish_lock); 
    std::cout<<"Notifies: "<<i<<std::endl; 

    auto it = pool.begin(); 
    while (it != pool.end()) { 
     auto state = it->wait_for(0ms); 
     if (state == std::future_status::ready) { 
      /* process result */ 
      it = pool.erase(it); 
     } else { 
      ++it; 
     } 
    } 
} 

回答

2

這取決於您的操作系統調度線程正在等待如何獲取互斥鎖。所有execute線程已經在第一個notify_one之前等待獲取互斥鎖,因此如果有一個簡單的先進先出線程隊列等待鎖定互斥鎖,那麼它們都會排在隊列中的main線程之前。當每個互斥鎖解鎖互斥鎖時,隊列中的下一個鎖定它。

這與互斥鎖比條件變量「更快」無關,條件變量必須鎖定相同的互斥鎖才能從等待中返回。

只要未來準備就緒,所有execute線程將從wait返回,並且所有嘗試鎖定該互斥鎖並加入等待者隊列。當條件變量開始等待時,互斥鎖被解鎖,並且其他線程之一(隊列前面的線程)獲得鎖定。它調用notify_one,這會導致條件變量嘗試重新鎖定互斥鎖,並加入隊列的後面。通知線程解鎖互斥鎖,並且隊列中的下一個線程獲取鎖,並調用notify_one(它不會執行任何操作,因爲條件變量已經通知並且正在等待鎖定互斥鎖)。然後,隊列中的下一個線程將獲取互斥鎖,依此類推。

看起來execute線程中的一個在第一次調用notify_one之前沒有足夠快地進入隊列,所以它最終位於條件變量後面的隊列中。

+0

那麼在notify_one()之後獲得結果的最好方法是檢查所有未來嗎? – Alex

+0

我不明白你在問什麼,對不起。如果你想在'notify_one'調用和條件變量喚醒之間建立一對一的對應關係,那麼你需要使用多個條件變量或者不同形式的同步。 –