0

同期化のためにブースト条件変数をテストするために次のコードを試していますが、このコードは同期します。しかし、ここでは何が問題なのか、4つの値しか表示されません。、どうすれば修正できますか?

Windows7でvs2012を使用する

前もって感謝します。

#include <iostream>
#include <queue>

#include "boost\thread.hpp"
#include "boost\timer.hpp"

using namespace std;

int counter;

boost::mutex m;
boost::condition_variable CworkDone;
bool workdone = true;

bool procOn = true;

void display()
{
while (procOn == true)
{
    boost::mutex::scoped_lock lock(m);      

    if (workdone)
    {
        cout<<counter<<endl;
        CworkDone.notify_one();
        workdone = false;
    }   
    else 
    {
        CworkDone.wait(lock);
    }
}

}

void increment()
{
for(int i = 0 ; i <10 ; ++i)
{

    boost::mutex::scoped_lock lock(m);

    if (!workdone)
    {
        boost::this_thread::sleep(boost::posix_time::millisec(500));
        ++counter;
        workdone = true;
        CworkDone.notify_one();
    }
    else
    {
        CworkDone.wait(lock);
    }
}
procOn = false;
}

   int main()
{
boost::thread dispthread(display);
boost::thread incthread(increment);
dispthread.join();
incthread.join();

}
4

4 に答える 4

6

問題は、クラスの生産者/消費者の問題です。現状のコードは実際には意味がありません。両側で待機するために単一の条件変数を使用することはできません(同じコンテキストで同じ条件でnotifyを呼び出してから待機することを忘れないでください)。

コードをリファクタリングして、2つの条件変数を使用する必要があります。1つはプロデューサー用、もう1つはコンシューマー用です。次に例を示します。

編集:正しいはずの純粋なc++11実装で更新されました。

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

using namespace std;

mutex mutex_;
condition_variable con_, prd_;
atomic_bool done_{false}, process_{false}; // possibly overkill, but hey-ho
int counter = 0; // protected resource

void consumer()
{
  for (;;)
  {
    unique_lock<mutex> lock(mutex_);       // acquire the mutex before waiting
    // this is the correct way to wait on the condition (as highlighted by @Dale)
    con_.wait(lock, []{ return process_.load() || done_.load(); });
    if (!done_.load())
    {
      cout << counter << endl;
      process_.store(false);               // we're done with this item
      lock.unlock();                       // release before notifying
      prd_.notify_one();
    }
    else
      break;                               // we're done completely
  }
}

void producer()
{
  unique_lock<mutex> lock(mutex_);
  for (int i = 0; i < 10; ++i)
  {
    this_thread::sleep_for(chrono::milliseconds(500));
    ++counter;
    process_.store(true);                 // indicate that there is data
    con_.notify_one();
    // wait for the consumer to complete
    prd_.wait(lock, []{ return !process_.load(); });
  }
  done_.store(true);                      // we're done
  lock.unlock();                          // release before notifying
  con_.notify_one();
}

int main(void)
{
  thread c(consumer);
  producer();
  c.join();
}

つまり、コンシューマーはその状態を待機し(そして、プロデューサーのみがこれを呼び出しますnotify())、プロデューサーは、何かを生成すると、この通知を呼び出して、クライアントをウェイクアップします。次に、クライアントはnotify()プロデューサーをウェイクアップするために呼び出します。

上記の更新されたアプローチは、前に強調したように、スプリアスウェイクアップの問題に悩まされることはありません。これにより、の必要性がなくなりtimed_waitます。

于 2013-02-07T13:57:04.470 に答える
3

Nimは、プロデューサーとコンシューマーの個別のcondition_variablesが必要であることについては正しかったのですが、条件を待機するためのその回答の元のコードに欠陥がありました[下記の注を参照]。そのため、vivekはtimed_waitを使用する必要がありました(望ましい解決策ではありません、ところで)。

Nimはその後コードを修復したので、C ++ 11を使用している場合は、Nimの回答のコードが適切な解決策です。

古いバージョンのC++で立ち往生している場合は、以下のコードが機能します。

condition_variableを使用するための一般的なイディオムは次のとおりです。

lock the mutex
while (not ready to go)
    condition_variable.wait()
do it

そして、はい、誤検知が発生する可能性があります。そのため、上記のコードは、condition_variable.wait()呼び出しから戻った後に「準備完了」を再テストします。

Nimが使用する関数オブジェクト(この場合はラムダ関数)でオーバーロードされたboost :: waitメソッドは、このイディオムを実装し、誤検知を無視します(これはまだ発生します)。これは、関数オブジェクトを使用して「準備完了」状態をテストするために実行され、ロックを再取得して関数を再テストするまで戻りません。

以下のコードは、C ++ 11より前で機能し、オブジェクトベースのブースト待機関数を使用せずに、実際に何が起こっているかを明確にします。

#include <boost/thread.hpp>

boost::mutex m;
boost::condition_variable cProducer;
boost::condition_variable cConsumer;
volatile bool readyToDisplay = false;
volatile bool done = false;

void display()
{
    boost::mutex::scoped_lock lock(m);
    while(!done)
    {
        while (!readyToDisplay)
        {
           cConsumer.wait(lock);
        }
        cout << counter << endl;
        readyToDisplay = false;
        cProducer.notify_one();
    }
}

void increment()
{
  while(!done)
  {
    boost::this_thread::sleep(boost::posix_time::millisec(500));
    boost::mutex::scoped_lock lock(m);
    while(readyToDisplay)
    {
       cProducer.wait(lock);
    }
    ++counter;
    readyToDisplay = true;
    done = (counter == 10);
    cConsumer.notify_one();
  }
}

int main()
{
    boost::thread dispthread(display);
    boost::thread incthread(increment);
    dispthread.join();
    incthread.join();
    return 0;
}

出力:

1
2
3
4
5
6
7
8
9
10

注:他の人がこの厄介な領域をナビゲートするのを助けるために(そして大騒ぎが何であったかを見るのを助けるために)、元の答えは表示方法でこれに似たコードを含んでいました:(行番号が追加され、いくつかの詳細は省略されています)。

1: while (!done)
2: {
3:   boost::mutex::scoped_lock lock(m);
4:   cConsumer.wait(lock);
5:   std::cout << counter << endl;
6:   cProducer.notify_one();
7: }

このスレッドが3行目を実行する直前に、他のスレッドがカウンターをインクリメントし、条件変数でnotify_oneを呼び出すとします。このスレッドが3行目でミューテックスをロックして4行目で待機すると、ウェイクアップする必要のある通知がすでに発生しているため、待機は永久にハングします。

于 2014-08-11T18:37:07.397 に答える
0

プログラムは4つではなく5つの値を表示します

0
1
2
3
4

あなたのループ

void increment()
{
for(int i = 0 ; i <10 ; ++i)

通知はここでのみ行われるため、10回のうち5回のみ通知します

if (!workdone)
{
    boost::this_thread::sleep(boost::posix_time::millisec(500));
    ++counter;
    workdone = true;
    CworkDone.notify_one();
}

そして、この条件は2分の1に当てはまります。

それを修正するために、すべてはあなたが何をしたいかに依存します。10回通知を受け取りたい場合は、カウンターを10から20に変更してください;-)

于 2013-02-07T22:16:18.197 に答える
-1

boost :: mutex :: scoped_lock lock(m);の前にboost :: this_thread :: sleep(boost :: posix_time :: millisec(10)をインクリメント関数に追加するだけです。少し同期の問題

于 2014-05-23T19:25:08.490 に答える