0

現在、C++でのマルチスレッドに関する書籍を読んでいます。ある章では、スレッドセーフなキューのソースコードをいくつか見つけました。それは、おおよそ次のように構築されています:値をキューにプッシュされている場合スレッドセーフキューと疑似ウェイク

template<typename T> 
class QueueThreadSafe 
{ 
private: 
    std::mutex m_mutex; 
    std::queue<T> m_dataQueue; 
    std::condition_variable m_dataCondition; 

public: 
    void push(T someValue) 
    { 
     std::lock_guard<std::mutex> guard(m_mutex); 
     m_dataQueue.push(someValue); 
     m_dataCondition.notify_one(); 
    } 

    void pop(T &retVal) 
    { 
     std::unique_lock<std::mutex> lock(m_mutex); 
     m_dataCondition.wait(lock, [this]{return !m_dataQueue.empty();}); 
     retVal = m_dataQueue.front(); 
     m_dataQueue.pop(); 
    } 
}; 

、データ条件が通知され、いくつかの(可能な)ポップでスレッドを待って作業を再開することができます。このシナリオで私が混乱しているのは、偽の目覚めです。同時に1つのスレッドに通知され、別のスレッドが同時に起動するとどうなりますか?もちろん、彼は空でないキューも見ています。このシナリオでは、2つの異なるスレッドが値をポップしようとしますが、おそらくは値が1つしかありません - 古典的な競合状態です。

ここで何か不足していますか?これを行うより良い方法はありますか?

+1

@ 1201ProgramAlarm:[条件変数の既知の動作](https://en.wikipedia.org/wiki/Spurious_wakeup)です。それらを最適化するために使用された戦略は、偶発的なウェークアップ信号に対して脆弱になり、プログラムロジックは、条件が満たされない場合に覚醒してスリープした後の状態を再チェックすることによってそれを処理する必要があります。 – ShadowRanger

+1

コメント - 一部のオペレーティングシステムでは、Windows [WaitForMultipleObjects()](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(英語))など、複数のオブジェクトを待機する汎用の「アトミック」機能があります。 v = vs.85).aspx)。キューの場合、2つのオブジェクトはミューテックスとセマフォである可能性があります。セマフォ "count"は、キュー内の要素の数を反映します。競争条件と偽の目覚ましは問題ではありません。 – rcgldr

+0

ミューテックスとセマフォに使用される[WaitForMultipleObjects()](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v = vs.85).aspx)の場合、関数はmutexとゼロ以外のセマフォ "count"の両方を待機し、呼び出し元に戻る前に非ゼロセマフォ "count"をデクリメントします。これは競争条件を排除するものの一部です。 – rcgldr

答えて

2

偽の覚醒は、目を覚ましたときに覚醒の状態が有効であることを確認する必要があることを意味します。 wait関数が渡されているので:待ち時間が満たされた場合

  1. 相互排他のロック、および
  2. 述語が決定し

行動つのスレッドが通知されたとき、「通常」もう1つは誤って通知されます。どちらかが問題ではありませんが、どちらが速いかは問題ありません。ロックが取得され、キューが空でないことが確認された後、先頭の要素がポップされてロックが解除されます。ロックのためにレースを失ったものは、より速いスレッドがロックを解除するまでロックを獲得しないので、既に空になっているキューを見て、スリープ状態に戻るという偽の起床だったと判断します。

重要なことに、偽のスレッドがロックのレースに勝ったかどうかは重要ではありません。スレッドの1つが正常に起きたかのように振る舞い(条件が真であり、期待どおりに動作していた)、まるで目が覚めたように振舞う(条件が偽であり、期待通りに待機した) 。

+0

私を混乱させたのは、待機操作の順序だった。私は最初に条件がチェックされていると思って、ロックが取得されます。もちろん、その逆です。 – Brotcrunsher

0

通知されたスレッドと起床したスレッドはキューからポップする機会が同じだと思いますが、CPUがどのようにスケジュールの決定を下すか(どちらが速いのか)によって異なります。

どのスレッドに権限が必要かを指定しない限り、実装を変更する必要があります。