13

条件変数とミューテックスでの使用法を理解するのに苦労しています。コミュニティが助けてくれることを願っています。私はwin32のバックグラウンドを持っているので、CRITICAL_SECTION、HANDLE、SetEvent、WaitForMultipleObjectなどで使用されていることに注意してください.

これは、c++11 標準ライブラリを使用した同時実行の最初の試みです。これは、ここにあるプログラム例の修正版です。

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


int _tmain(int argc, _TCHAR* argv[])
{   
    std::queue<unsigned int>    nNumbers;

    std::mutex                  mtxQueue;
    std::condition_variable     cvQueue;
    bool                        m_bQueueLocked = false;

    std::mutex                  mtxQuit;
    std::condition_variable     cvQuit;
    bool                        m_bQuit = false;


    std::thread thrQuit(
        [&]()
        {
            using namespace std;            

            this_thread::sleep_for(chrono::seconds(7));

            // set event by setting the bool variable to true
            // then notifying via the condition variable
            m_bQuit = true;
            cvQuit.notify_all();
        }
    );

    std::thread thrProducer(
        [&]()
        {           
            using namespace std;

            int nNum = 0;
            unique_lock<mutex> lock( mtxQuit );

            while( ( ! m_bQuit ) && 
                   ( cvQuit.wait_for( lock, chrono::milliseconds(10) ) == cv_status::timeout ) )
            {
                nNum ++;

                unique_lock<mutex> qLock(mtxQueue);
                cout << "Produced: " << nNum << "\n";
                nNumbers.push( nNum );              
            }
        }
    );

    std::thread thrConsumer(
        [&]()
        {
            using namespace std;            

            unique_lock<mutex> lock( mtxQuit );

            while( ( ! m_bQuit ) && 
                    ( cvQuit.wait_for( lock, chrono::milliseconds(10) ) == cv_status::timeout ) )
            {
                unique_lock<mutex> qLock(mtxQueue);
                if( nNumbers.size() > 0 )
                {
                    cout << "Consumed: " << nNumbers.front() << "\n";
                    nNumbers.pop();
                }               
            }
        }
    );

    thrQuit.join();
    thrProducer.join();
    thrConsumer.join();

    return 0;
}

これについていくつか質問があります。

「std::condition_variable を待機するスレッドは、最初に std::unique_lock を取得する必要がある」と読みました。

したがって、いつ終了が通知されたかを示す {quit mutex, condition variable & bool} があります。次のように、プロデューサー スレッドとコンシューマー スレッドはそれぞれ std::unique_lock を取得する必要があります。

std::unique_lock<std::mutex> lock(m_mtxQuit);

これは私を混乱させています。これにより、最初のスレッドで終了ミューテックスがロックされ、2 番目のスレッドがブロックされませんか? それが本当なら、最初のスレッドはどのようにしてロックを解放し、他のスレッドが開始できるようにするのでしょうか?

もう 1 つの質問: wait_for() 呼び出しを 0 秒待機するように変更すると、そのスレッドは枯渇します。誰か説明できますか?while ループを実行する前にブロックしないことを期待します (タイムアウトの代わりに no_timeout が recv されると仮定するのは正しいですか?)。

wait_for() を呼び出してゼロ時間を指定して、wait_for() 呼び出しがブロックされず、代わりに条件をチェックして続行するにはどうすればよいですか?

また、この件に関する優れた参考文献についても知りたいと思います。

4

2 に答える 2

12

これにより、最初のスレッドで終了ミューテックスがロックされ、2 番目のスレッドがブロックされませんか?

はい。

それが本当なら、最初のスレッドはどのようにしてロックを解放し、他のスレッドが開始できるようにするのでしょうか?

あなたがそれを待っているとき、condition_variableそれはあなたがそれを渡したロックを解除します。

cvQuit.wait_for( lock, chrono::milliseconds(10) )

条件変数が呼び出さlock.unlock()れ、最大 10 ミリ秒ブロックされます (これはアトミックに発生するため、ミューテックスのロックを解除してから条件の準備が整い、それを見逃す可能性があるブロックの間にウィンドウはありません)。

ミューテックスがロック解除されると、他のスレッドがミューテックスのロックを取得できるようになります。

もう 1 つの質問: wait_for() 呼び出しを 0 秒待機するように変更すると、そのスレッドは枯渇します。誰か説明できますか?

ミューテックスは、他のスレッドがロックするのに十分な時間ロック解除されていないため、他のスレッドが不足している予想されます。

タイムアウトの代わりに no_timeout が受信されたと仮定するのは正しいですか?

いいえ、条件が準備できずに時間が経過すると、ゼロ秒後でも「タイムアウト」します。

wait_for() を呼び出してゼロ時間を指定して、wait_for() 呼び出しがブロックされず、代わりに条件をチェックして続行するにはどうすればよいですか?

条件変数を使用しないでください。条件が真になるのを待ちたくない場合は、条件変数を待たないでください! テストm_bQuitして続行するだけです。(さておき、なぜあなたのブール値は と呼ばれているのm_bXxxですか?それらはメンバーではないので、m_接頭辞は誤解を招くものであり、b接頭辞はハンガリー表記のひどいMSの習慣のように見えます...悪臭を放ちます。)

また、この件に関する優れた参考文献についても知りたいと思います。

最も参考になるのは、Anthony Williams のC++ Concurrency In Actionで、C++11 のアトミックとスレッド ライブラリ全体、およびマルチスレッド プログラミングの一般原則を詳細にカバーしています。このテーマに関する私のお気に入りの本の 1 つは、Pthreads に固有の Butenhof のProgramming with POSIX Threadsですが、C++11 の機能は Pthreads に非常によく似ているため、その本の情報を C++11 マルチスレッドに簡単に転送できます。 .

注: ミューテックスで保護せずthrQuitに書き込む場合m_bQuit、別のスレッドがその書き込みと同時に読み取ることを妨げるものは何もないため、競合状態、つまり未定義の動作です。bool への書き込みは、ミューテックスによって保護されているか、アトミック型である必要があります。std::atomic<bool>

2 つのミューテックスは必要ないと思います。競合が増えるだけです。2 番目のミューテックスを持つポイントがないmtxQuitのを待機している間は決して解放しないため、一度に 1 つのスレッドだけがクリティカル セクションに入ることができることが既に保証されています。condition_variablemtxQuit

于 2012-11-14T00:53:55.113 に答える
2

何かをチェックして、真であるかどうかに関係なく続行する場合 (おそらく 2 つの異なることを行う場合)、条件変数を使用するのは不適切です。条件変数は、ロックされたデータ構造に関連付けられた条件の低レベルのプリミティブであり、ロックの取得と解放をスピンすることなく待機する必要があります。標準的な例はキューです。キューへのアクセスを保護するロックと、2 つの条件変数 (キューが空ではなく、キューがいっぱいではない) があります。キューに何かをプッシュするには、ロックを取得し、満杯でないことを確認し、満杯でない場合は condvar を待機し、値をキューにプッシュし、空ではない condvar に通知します (空ではないため)。ロックを解除します。ポップ操作も同様です。

したがって、あなたの場合、満杯にならない単純なキューがあるため、1 つのロックと 1 つの condvar が必要です。完全に理にかなっています。しかし、その後、トリガーの完了が必要な「終了」フラグがあります。終了フラグが設定されるのを待ちたくない - 設定されるまで実際に作業をしたい - したがって、ここでは condvar は本当に意味がありません。はい、それを機能させる複雑な配置を考え出すことはできますが、条件変数を条件変数として使用していないため、混乱を招きます。

std::atomic<bool>終了フラグにa を使用する方がより理にかなっています (そしてより明確です) 。次に、それを false に初期化し、終了スレッドで true に設定し、他のスレッドでチェックします。

于 2012-11-14T00:53:20.637 に答える