我有以下情况:
int main(){
data_t data;
std::thread data_producer([&data](){
while(true){
data = produce_data();
std::this_thread::sleep_for(1s);
}
});
auto print_data = [&data](){
std::cout << data.as_string() << "\n";
});
print_data();
//do some stuff
print_data();
print_data();
//do some stuff
print_data();
//.....
}
如您所见,数据生成器永远运行,尝试生成数据并每次覆盖旧数据。
当我致电print_data()
时,将打印出最后生成的数据。但是,如果print_data
比data_producer
快,则相同的数据将打印两次。
如何防止此行为?换句话说,我希望阻止print_data()
,直到有来自制作人的新数据。
另一方面,我不介意是否生成了数据并且没有人打印出来。但我想介意,如果生产日期不止一次打印过吗?
我尝试将std::condition_variable
与std::mutex
一起使用。我想出了这个解决方案。我在某些情况下测试了它并且它正在工作但我不确定它是否正确:
int main(){
std::mutex m;
std::condition_variable cv;
bool ready{ false };
data_t data;
std::thread data_producer([&data](){
while(true){
{
std::lock_guard<std::mutex> lk(m);
data = produce_data();
ready = true;
}
cv.notify_one()
std::this_thread::sleep_for(1s);
}
});
auto print_data = [&data](){
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [&]{return ready;});
std::cout << data.as_string() << "\n";
ready = false;
});
print_data();
//do some stuff
print_data();
print_data();
//do some stuff
print_data();
//.....
}
虽然它在我做的测试中按照我的预期工作,但我非常怀疑我的解决方案是垃圾。
这是正确的方法吗?
答案 0 :(得分:2)
这里有一堆问题,代码是给定的。
您访问data
的方式会导致并发问题。您需要通过锁保护变量读取和写入。它似乎是堆栈上的结构。但是如上所述,您冒着(1)在阅读器中看到陈旧值的风险,因为运行阅读器的CPU没有写作者所做的最新视图(这是一个可见性问题)(2) )看到date
的不完整或奇怪的值,因为作者在更新过程中被停止了。如果data
是一个大型结构,那么这是双重的,因为副本更可能被中断,而不仅仅是一个指针写入,这本身就是原子的。但是你可能最终会看到构造中的不完整对象(有关详细信息,请参阅safe initialization and publication)。
对于您的主要问题,您需要密切关注制作人发布的内容。你可以有一个计数器,每次发布新数据时都会增加。阅读器还存储其打印的最后数据的计数器,并且仅在当前计数器较高时打印数据。
代码在伪代码中看起来像这样:
mutex lock;
int counter = 0;
data_t data = null;
int last_saw = -1;
\\ in writer
data_t new_data = produce_data;
lock.acquire();
counter++;
data = new_data;
lock.release();
\\ in reader
data_t to_print = null;
lock.acquire();
if (counter > lastSaw) {
to_print = data;
last_saw = counter;
}
lock.release();
if (to_print != null)
count << to_print << endl;