简体   繁体   English

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

[英]Synchronize two threads to the same timer

I have the following scenario: 我有以下情况:

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

As you see, the data producer runs forever, trying to produce data and overwrite the old one each time. 正如你看到的,数据生产者将永远运行下去,试图生产数据,每次覆盖旧的。

When I call print_data() , the last produced data would be printed out. 当我调用print_data() ,最后生成的数据将被打印出来。 However, if print_data was faster than the data_producer , the same data will be printed twice. 但是,如果print_datadata_producer快,则相同的数据将被打印两次。

How can I prevent this behavior? 如何防止这种行为? In other words, I want print_data() to be blocked until there is a new data from producer. 换句话说,我希望在生产者有新数据之前,将print_data()阻塞。

On the other hand, I do not mind if a data was produced and no one printed it. 另一方面,我不介意是否生成了数据并且没有人打印它。 But I mind if the produced date was printed more than once? 但我介意的生产日期被印不止一次?

I tried to use std::condition_variable with std::mutex . 我试图将std::condition_variablestd::mutex I came up with this solution. 我想出了这个解决方案。 I tested it on some scenarios and it was working but I am not sure if it is correct: 我在某些情况下对其进行了测试,并且可以正常工作,但不确定是否正确:

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

Although it worked as I expected in the tests I did, I highly suspect that my solution is rubbish. 尽管它按我在测试中所预期的那样工作,但我高度怀疑我的解决方案是垃圾。

Is it the right way? 这是不是正确的方式?

There's a bunch of issues here with the code as given. 这里给出的代码存在很多问题。

The way you're accessing data lends itself to concurrency issues. 您访问data的方式很容易导致并发问题。 You need to protect the variables reads and writes via a lock. 您需要通过锁保护变量的读写。 It seems to be a structure on the stack. 它似乎是堆栈上的结构。 But as given, you run the risk of (1) seeing stale values in the reader because the CPU running the reader doesn't have an up to date view of what the writer has done (this is a visibility issue) (2) see incomplete or strange values of date because the writer was stopped in the middle of an update. 但是,正如给定的那样,您冒着以下风险:(1)在阅读器中看到过时的值,因为运行阅读器的CPU没有关于编写器所做操作的最新视图(这是可见性问题)(2) date值不完整或奇怪,因为编写器在更新过程中停止了。 If data is a large struct this is doubly true as the copy is more likely to be interrupted rather than a just a pointer write, which is atomic by itself. 如果data是大结构,则这是双重事实,因为复制更有可能被中断,而不仅仅是指针写入,而指针写入本身就是原子的。 But you could end up seeing an incomplete object from construction (see safe initialization and publication for more details). 但是您最终可能会看到构造中的对象不完整(有关更多详细信息,请参见安全的初始化和发布 )。

For your main problem, you either need to keep tabs on what has been published by the producer. 对于您的主要问题,您要么需要保持制片人已经发布的内容的标签。 You could have a counter which gets incremented every time new data is published. 您可能有一个计数器,该计数器在每次发布新数据时都会增加。 The reader also stores the counter of the last data which it printed, and will only print data when the current counter is higher. 阅读器还会存储其最后打印的数据的计数器,并且仅在当前计数器较高时才打印数据。

The code might look like this in pseudocode: 该代码可能类似于伪代码:

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM