2017-05-09 5 views
0

私はcondition_variablesを理解しようとしています。スレッドはmutexをstd :: conditional_variable :: wait()より速くロックします

私は私のコードは次のように動作する必要がありますね:
3.スレッドが
4.スレッドが
5に通知送信MXをロックするロック解除ここ< =通知)
1.メインロックMX
2.メイン待ち時間(スレッドのロック解除mx
メインwait()が終了してロックmx

なぜスレッドは通知後にwait()呼び出しよりも速くmxをロックできますか?
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

これは、ミューテックスロックの取得を待機している方法を使用しているOSのスケジュールスレッドに依存します。すべてのexecuteスレッドはすでに最初のnotify_one前にmutexロックを取得するために待機しているので、ミューテックスをロックするために待機しているスレッドの簡単なFIFOキューがありますならば、それらはすべて先に、キュー内のmainスレッドのです。各mutexがmutexをロック解除すると、その次のmutexがmutexをロックします。

これは、条件変数よりも「高速」なmutexとは関係がありません。条件変数は、同じmutexをロックしてwaitから戻る必要があります。

のスレッドがすべて準備完了するとすぐに、waitのスレッドから戻り、すべてのミューテックスをロックして、ウェイターのキューに参加しようとします。条件変数が待機を開始すると、mutexはロック解除され、他のスレッド(キューの前面にあるスレッド)の1つがロックを取得します。 notify_oneを呼び出します。これにより、条件変数はmutexを再ロックしようとし、キューの後ろに参加します。通知スレッドはmutexのロックを解除し、キュー内の次のスレッドはロックを取得し、notify_oneを呼び出します(条件変数がすでに通知され、mutexのロックを待機しているため何もしません)。次に、キュー内の次のスレッドがミューテックスを取得します。

executeスレッドのうちの1つが、最初のnotify_oneコールの前にキューに入るのに十分速く実行されなかったようです。そのため、条件変数の後ろのキューに入ります。

+0

notify_one()の後に結果を得る最良の方法は、すべての未来をチェックすることです。 – Alex

+0

あなたが何を求めているのか分かりません。申し訳ありません。 'notify_one'呼び出しと条件変数の起動との間に1対1の対応が必要な場合は、複数の条件変数または異なる形式の同期を使用する必要があります。 –

関連する問題