2017-04-22 9 views
0

ここでは、複数のスレッドから呼び出されるコードがあります。 std::condition_variable::waitの述語はスレッドごとに少し異なりますが、問題は変わりません。std :: condition_variable :: notify_allの後に複数のスレッドがstd :: unique_lock <std::mutex>をどのように再獲得するかを注文します。

std::unique_lock<std::mutex> lock{connection_mutex}; 
cv.wait(lock, 
    [conn = shared_from_this()] 
    { 
     return conn->connection_is_made(); 
    } 
); 

//do some stuff 
lock.unlock(); 

私はcppreference

でこれを読んで条件変数を通知し、タイムアウトの期限が切れる、またはスプリアスウェイクアップが発生した場合、スレッドが起こされると、ミューテックスがアトミックに再取得されます。スレッドは、条件をチェックし、起床が偽である場合は待機を再開します。このことから

私は述語ラムダがチェックされ、それがtrueを返す場合lockがロックされたままになり、私はこのmutexの保護の下でいくつかのものをし続けることができるよりも、lockがロックされることを理解しています。 std::condition_variablenotify_one()メンバー機能によって通知されると、それは完璧な意味を持ちます。

std::condition_variablenotify_all()が通知されるとどうなりますか?すべてのスレッドが起動してミューテックスをロックするために "行内で待機"してから、述語が返すものをチェックするか、あるいは何か他のことを行うかどうかを確認するだけで、ドキュメントで見つけることはできません。


EDIT:以下のコメントを見て少し考えました。 std::condition_variable::waitは、第1引数にstd::unique_lockがあり、通知後にstd::condition_variable::waitが呼び起こされた場合 - std::unique_lockが再取得されます。複数のスレッドが同じ通知を待っている場合は、notify_all()が最後に呼び出されたときよりstd::condition_variableスレッドは1つのスレッドしかmutexをロックできず、他のすべてのスレッドはスリープ状態に戻ります。だから効率が低い以外はnotify_one()と同じ効果がある場合は、notify_all()メンバ関数を持っていても意味がありません。私は、 のスレッドをstd::condition_variable::waitに戻すためにmutexを再度取得しなければならない場合、待機中のすべてのスレッドが同時にそれを行うことができないことを意味します。

+0

通知の順序について質問している場合、これは役立つ可能性があります。http:// stackoverflow。com/questions/15912322/condition-variable-deadlock – didiz

+0

回答ありがとうございます。この場合、私はnotify_all()シグナルを受け取ることはないと心配していません。私は、同じcondition_variableで待機するときに複数のスレッドがどのように動作するのかを知りたいだけで、notify_all()で通知を受け取り、同じmutexを取得しようとします。 – etrusks

+2

OSスケジューラに任されています。どちらのスレッドが最初にミューテックスを取得するかは分かりません。 – 1000ml

答えて

0

私は、すべてのスレッドがnotify_all()の呼び出しの後に目を覚ましているかどうかを確認するためにいくつかのテストを行いました。

#include <iostream> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <chrono> 

std::mutex M; 
std::condition_variable CV; 
int var = 0; 


int main() 
{ 
    std::thread t1{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 1){ 
       CV.wait(lck); 
       std::cout << "t1 woken up and (i != 1) = " 
          << (var != 1) << std::endl; 
      } 
     } 
    }; 

    std::thread t2{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 2){ 
       CV.wait(lck); 
       std::cout << "t2 woken up and (i != 2) = " 
          << (var != 2) << std::endl; 
      } 
     } 
    }; 

    std::thread t3{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 3){ 
       CV.wait(lck); 
       std::cout << "t3 woken up and (i != 3) = " 
          << (var != 3) << std::endl; 
      } 
     } 
    }; 

    std::thread t4{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 4){ 
       CV.wait(lck); 
       std::cout << "t4 woken up and (i != 4) = " 
          << (var != 4) << std::endl; 
      } 
     } 
    }; 

    for(int i = 0; i < 6; ++i){ 
     std::unique_lock<std::mutex> lck{M}; 
     var = i; 
     CV.notify_all(); 
     lck.unlock(); 
     std::this_thread::sleep_for(std::chrono::seconds{1}); 
     std::cout << "\n\n"; 
    } 

    t1.join(); 
    t2.join(); 
    t3.join(); 
    t4.join(); 
} 

そしてここでの結果は、だから私は関係なく、第一のウェイクアップされているもの、スレッド、すべての通知スレッドが単一でいくつかのランダムな順序でウェイクアップされていないことを見ることがうれしいです

t3 woken up and (i != 3) = 1 //spurious wakeup 


t3 woken up and (i != 3) = 1 
t4 woken up and (i != 4) = 1 
t2 woken up and (i != 2) = 1 
t1 woken up and (i != 1) = 0 


t3 woken up and (i != 3) = 1 
t4 woken up and (i != 4) = 1 
t2 woken up and (i != 2) = 0 


t3 woken up and (i != 3) = 0 
t4 woken up and (i != 4) = 1 


t4 woken up and (i != 4) = 0 

ですnotify_all()に電話してください。スレッドAのように起床したスレッドの1つが、何らかの仕事をした後にmutexのロックを解除したときのように見えます。notify_all()と同じ呼び出しで呼び起こされた次のスレッドがスレッドAとして自動的にロックしています(mutex)ただロック解除されています。これは、notify_all()によって目覚めされたすべてのスレッドが、共有データを保護するために使用された同じmutexをロック/ロック解除するまで続きます。