将两个线程同步到同一个计时器

时间:2018-01-05 12:57:52

标签: c++ multithreading c++11 condition-variable

我有以下情况:

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_datadata_producer快,则相同的数据将打印两次。

如何防止此行为?换句话说,我希望阻止print_data(),直到有来自制作人的新数据。

另一方面,我不介意是否生成了数据并且没有人打印出来。但我想介意,如果生产日期不止一次打印过吗?

我尝试将std::condition_variablestd::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();
    //.....
}

虽然它在我做的测试中按照我的预期工作,但我非常怀疑我的解决方案是垃圾。

这是正确的方法吗?

1 个答案:

答案 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;