0

我正在尝试使用以下代码来测试同步提升条件变量,此代码确实同步。但它只显示 4 个值这里有什么问题?,我该如何解决?

在 Windows 7 上使用 vs 2012

提前致谢。

#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

问题是类生产者/消费者问题。您的代码实际上没有任何意义-您不能使用单个条件变量来等待双方,(请记住,您先调用通知然后等待-在相同的条件下,在相同的上下文中。)所以任何事情都可能发生。

您需要重构代码以使用两个条件变量,一个用于生产者,一个用于消费者,例如:

编辑:使用应该正确的纯 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 使用的函数对象(在本例中为 lambda 函数)重载的 boost::wait 方法实现了这个习惯用法,包括忽略误报(仍然会发生)。它使用函数对象来测试“ready-to-go”条件,并且在重新获取锁并重新测试函数之前不会返回。

下面的代码在 C++ 11 之前工作,并且没有使用基于函数对象的 boost wait 来明确实际发生的情况。

#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

该程序显示 5 个值而不是 4 个

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();
}

这个条件是二分之一。

要解决它,一切都取决于你想做什么。如果您想收到 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 回答