2016-12-30 8 views
1

返しwait_forこれは私がスレッドを開始して行をコメント場合http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/C++ condition_variable即座

から簡単なコードはなぜwait_for()瞬時に戻るんでしょうか?このよう

// condition_variable::wait_for example 
#include <iostream>   // std::cout 
#include <thread>    // std::thread 
#include <chrono>    // std::chrono::seconds 
#include <mutex>    // std::mutex, std::unique_lock 
#include <condition_variable> // std::condition_variable, std::cv_status 

std::condition_variable cv; 

int value; 

void read_value() { 
    std::cin >> value; 
    cv.notify_one(); 
} 

int main() 
{ 
    std::cout << "Please, enter an integer (I'll be printing dots): "; 
    //std::thread th (read_value); 

    std::mutex mtx; 
    std::unique_lock<std::mutex> lck(mtx); 
    while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) { 
    std::cout << '.'; 
    } 
    std::cout << "You entered: " << value << '\n'; 

    //th.join(); 

    return 0; 
} 

更新:

この例では他の問題(関連するバッファリングCOUT ...)を探してしないでください。元の質問はwait_forがスキップされた理由です。

+0

プログラムを起動する前に、LD_PRELOAD =/lib/x86_64-linux-gnu/libpthread.so.0を使って、自動的にpthread libをロードしないようです。 何が起こったのですか? – Yuri

+2

ビルドに使用したコマンドラインを表示します。実行の出力を表示します。 –

答えて

0

は、このコードでいくつかの問題があります。

まず、あなたが気づいているように、プログラムは-pthreadオプションでビルドする必要があります。

第2に、ドットが印刷されていることを確認するには、出力をフラッシュする必要があります。

最も重要なことは、mutexとcondition変数の使用が全く間違っていることです。条件変数通知は、ユーザー指定の述語/条件の値の変更を示します。条件の変更および検査はアトミックおよびシリアライズでなければなりません。そうでなければデータ競合があり、プログラムの動作は未定義です。

valueは、2つのスレッドによって読み書きされますが、並行処理の制御メカニズムがないか、別の言い方をすると、操作の間に「発生前」の関係はありません。value書き込みはvalueとなります。

修正例は、以下:

  • ミューテックスは、(例示のため)グローバルスコープに移動されるので、valueを読み取りスレッド:だから

    // condition_variable::wait_for example 
    #include <chrono>    // std::chrono::seconds 
    #include <condition_variable> // std::condition_variable, std::cv_status 
    #include <iostream>   // std::cout 
    #include <mutex>    // std::mutex, std::unique_lock 
    #include <thread>    // std::thread 
    
    std::mutex mtx; 
    std::condition_variable cv; 
    
    int value; 
    
    void read_value() { 
        int v; 
        std::cin >> v; 
        std::unique_lock<std::mutex> lck(mtx); 
        value = v; 
        cv.notify_one(); 
    } 
    
    int main() { 
        std::cout << "Please, enter an integer (I'll be printing dots): "; 
        std::thread th(read_value); 
    
        std::unique_lock<std::mutex> lck(mtx); 
        while (cv.wait_for(lck, std::chrono::seconds(1)) == std::cv_status::timeout) { 
        std::cout << '.' << std::flush; 
        } 
        std::cout << "You entered: " << value << '\n'; 
    
        th.join(); 
    
        return 0; 
    } 
    

    、変更は何valueを変更するために、ロックすることができます。

  • 読み取りは別の変数にあります。 valueに直接入力することはできません。入力フォームstd::cinから待っている間にmutexを保護しながらmutexを保護しなければならないので、メインスレッドはドットを印刷できません。タイムアウト時にmutexを取得しようとします。 。各ドットを出力した後
  • std::coutがフラッシュさ
+0

mutexは、 'cv.wait_for()'の呼び出しで解放されます。 – chill

+0

しかし、@selbie(潜在的に、私はそれを観察しない)によって指摘されているように、偽の起床には問題があります。 – chill

4

短い答えは:-pthreadでコンパイルすると、あなたの問題が離れて行くでしょう。

更新This is a confirmed bug/issue in libstdc++.コンパイラフラグとして渡される-pthreadずに、時限待機コールはすぐに戻ります。問題の歴史(3年)を考えると、すぐに修正される可能性は低いです。とにかく、偽の起床問題を避けるために述語に条件変数を使用する理由について、私のメッセージを読んでください。あなたがposixスレッドライブラリにリンクしている場合でも、それはまだ当てはまります。

cplusplus.comのサンプルコードにはいくつかの問題があります。このようなことには

std::cout << '.'; 

:まず第一に、この行を修正

std::cout << '.'; 
std::cout.flush() 

stdoutがフラッシュになっていない場合はそうでない場合、あなたは任意のドットは表示されません。

あなたはこのように(コメントアウトスレッドに)あなたのプログラムをコンパイルする場合:

g++ yourcode.cpp -std=c++11 

次いで、得られたa.outのプログラムは、スレッドを使用しない場合、あなたが説明されている問題を呈します。つまり、スレッドが使用されていないときは偽の起床があります。何らかの不明な情報源からの条件変数で呼び出されるファントムnotify()のようなものです。これは奇妙ですが、不可能ではありません。

しかし、すぐにあなたがスレッド変数の宣言をコメント解除して、プログラムは、マルチスレッドを使用していないプログラムの結果として例外(クラッシュ)をスローします:

terminate called after throwing an instance of 'std::system_error' 
    what(): Enable multithreading to use std::thread: Operation not permitted 
Please, enter an integer (I'll be printing dots): Aborted (core dumped) 

興味深いが、そうしてみましょう-pthread

g++ yourcode.cpp -std=c++11 -pthread 

今すぐすべてを再コンパイルすることでこれを修正したり、スレッドなしで期待通りに動作します。それ以上の偽の目覚めは見えません。

ここで、あなたが見ている動作をなぜ見ているのか話しましょう。 条件変数を使用しているプログラムは、疑似ウェークアップを処理するために常に記述する必要があります。そして、好ましくは、述部ステートメントを使用します。つまり、ファントムがwaitまたはwait_forステートメントを早期に返すように通知することがあります。 cplusplus.comのウェブ上のサンプルコードでは述語を使用していませんし、この可能性についても扱っていません。

次のようにのは、それを修正してみましょう:

変更のコードブロックを:

while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) { 
    std::cout << '.'; 
    } 

このことには:

while (cv.wait_for(lck,std::chrono::seconds(1), condition_check)==false) { 
    std::cout << '.'; 
    std::cout.flush(); 
} 

そして他の場所mainの外側が、宣言した後value、この機能を追加:

bool condition_check() { 
    return (value != 0); 
} 

これで、1秒ごとにウェイト・ループが起きるか、入力スレッドによってnotifyが呼び出されたときに起きるでしょう。待機ループはvalue != 0まで続きます。 (技術的には、valueはスレッド間で、ロックまたはstd :: atomic値のどちらかで同期する必要がありますが、これは細部です)。

今や謎はwait_forの非述語バージョンが偽のwake_up問題に苦しんでいる理由です。私の推測では、マルチスレッドのランタイム(-pthread)を使用したときに消えてしまうシングルスレッドのC++ランタイムの問題です。おそらく、condition_variableは、posixスレッドライブラリがリンクされているときに、動作や実装が異なります。

+0

-lpthreadを追加しても同じ問題です。リンカーは-lpthreadを追加してもpthreadライブラリをリンクしません。しかし、LD_PRELOAD pthreadを使ってプログラムを実行すると、期待通りに動作します。これは私のツールチェーンのバージョン/構成に関連する問題だと思われます。 2つのマシンで同じツールチェーンバージョンでテストされています。 (g ++(Ubuntu 5.4.0-6ubuntu1〜16.04.4)5.4.0 20160609 ) – Yuri

関連する問題