我正在尝试使用代码来测试同步化的boost条件变量, 这段代码确实同步。但它只显示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();
}
答案 0 :(得分:6)
问题是班级生产者/消费者问题。你的代码实际上没有意义 - 你不能使用单个条件变量来等待双方,(记住你在相同的情况下调用notify然后等待 - 在相同的条件下。)所以任何事情都可能发生。 / p>
您需要重构代码以使用两个条件变量,一个用于生产者,一个用于消费者,例如:
编辑:更新了纯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
的需求。
答案 1 :(得分:3)
Nim对于需要单独的生产者和消费者condition_variables是正确的,但是等待条件的答案中的原始代码是有缺陷的[见下面的注释]这就是为什么vivek需要使用timed_wait(不是理想的解决方案,BTW)。
Nim已经修复了代码,所以如果您使用的是C ++ 11,Nim的答案中的代码是一个很好的解决方案。
如果你遇到旧版本的C ++,下面的代码就可以了。
使用condition_variable的常用习惯是:
lock the mutex
while (not ready to go)
condition_variable.wait()
do it
并且,是的,误报是可能的,这就是为什么上面的代码重新测试&#34;准备好去&#34;从condition_variable.wait()调用返回后。
使用Nim使用的函数对象(在本例中为lambda函数)重载的boost :: wait方法实现了这个习惯用法,包括忽略误报(仍然会发生)。它使用函数对象来测试&#34;准备就绪&#34;条件,并且在重新获得锁定并重新测试该函数之前不会返回。
下面的代码适用于C ++ 11之前的版本,并且不使用基于函数对象的boost等待,以明确实际发生的事情。
#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
注意:为了帮助其他人浏览这个棘手的区域(并看看大惊小怪),原始答案在显示方法中包含与此类似的代码:(添加了行#&#39;并省略了一些细节。)
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行等待时,应该使其唤醒的通知已经发生,因此等待将永远挂起。
答案 2 :(得分: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; - )
答案 3 :(得分:-1)
之前只需添加boost :: this_thread :: sleep(boost :: posix_time :: millisec(10) boost :: mutex :: scoped_lock lock(m); 在增量函数中。 一点点同步问题