C ++ - 多线程 - 线程之间的通信

时间:2017-01-06 12:11:33

标签: c++ multithreading condition-variable

#include <iostream>
#include <thread>
#include <condition_variable>
#include <queue>
#include <cstdlib>
#include <chrono>
#include <ctime>
#include <random>

using namespace std;

//counts every number that is added to the queue
static long long producer_count = 0;
//counts every number that is taken out of the queue
static long long consumer_count = 0;

void generateNumbers(queue<int> & numbers, condition_variable & cv, mutex & m, bool & workdone){
    while(!workdone) {
        unique_lock<std::mutex> lk(m);
        int rndNum = rand() % 100;
        numbers.push(rndNum);
        producer_count++;
        cv.notify_one();
     }
}

void work(queue<int> & numbers, condition_variable & cv, mutex & m, bool & workdone) {
    while(!workdone) {
        unique_lock<std::mutex> lk(m);
        cv.wait(lk);
        cout << numbers.front() << endl;
        numbers.pop();
        consumer_count++;

     }
}

int main() {
    condition_variable cv;
    mutex m;
    bool workdone = false;
    queue<int> numbers;

    //start threads
    thread producer(generateNumbers, ref(numbers), ref(cv), ref(m),     ref(workdone));
    thread consumer(work, ref(numbers), ref(cv), ref(m), ref(workdone));

    //wait for 3 seconds, then join the threads
    this_thread::sleep_for(std::chrono::seconds(3));
    workdone = true;

    producer.join();
    consumer.join();

    //output the counters
    cout << producer_count << endl;
    cout << consumer_count << endl;

    return 0;
}

大家好, 我尝试用C ++实现Producer-Consumer-Pattern。 生产者线程生成随机整数,将它们添加到队列中,然后通知消费者线程已添加新数字。

使用者线程等待通知,然后将队列的第一个元素打印到控制台并删除它。

我为每个添加到队列中的数字增加了一个计数器,并为从队列中取出的每个数字增加了另一个计数器。

我希望这两个计数器在程序完成后保持相同的值,但差别很大。 表示队列添加的计数器总是在百万范围内(我上次测试中为3871876),表示从队列中取出数字的消费者的计数器总是低于100k(在我上次测试中为89993)。

有人可以向我解释为什么会有这么大的差异吗? 我是否必须添加另一个条件变量,以便生产者线程也等待消费者线程? 谢谢!

2 个答案:

答案 0 :(得分:3)

不需要第二个std::condition_variable,只需重复使用您的第二个std::atomic<bool>即可。如其他人所述,您应该考虑使用bool而不是普通#include <iostream> #include <thread> #include <condition_variable> #include <queue> #include <cstdlib> #include <chrono> #include <ctime> #include <random> #include <atomic> //counts every number that is added to the queue static long long producer_count = 0; //counts every number that is taken out of the queue static long long consumer_count = 0; void generateNumbers(std::queue<int> & numbers, std::condition_variable & cv, std::mutex & m, std::atomic<bool> & workdone) { while(!workdone.load()) { std::unique_lock<std::mutex> lk(m); int rndNum = rand() % 100; numbers.push(rndNum); producer_count++; cv.notify_one(); // Notify worker cv.wait(lk); // Wait for worker to complete } } void work(std::queue<int> & numbers, std::condition_variable & cv, std::mutex & m, std::atomic<bool> & workdone) { while(!workdone.load()) { std::unique_lock<std::mutex> lk(m); cv.notify_one(); // Notify generator (placed here to avoid waiting for the lock) cv.wait(lk); // Wait for the generator to complete std::cout << numbers.front() << std::endl; numbers.pop(); consumer_count++; } } int main() { std::condition_variable cv; std::mutex m; std::atomic<bool> workdone(false); std::queue<int> numbers; //start threads std::thread producer(generateNumbers, std::ref(numbers), std::ref(cv), std::ref(m), std::ref(workdone)); std::thread consumer(work, std::ref(numbers), std::ref(cv), std::ref(m), std::ref(workdone)); //wait for 3 seconds, then join the threads std::this_thread::sleep_for(std::chrono::seconds(3)); workdone = true; cv.notify_all(); // To prevent dead-lock producer.join(); consumer.join(); //output the counters std::cout << producer_count << std::endl; std::cout << consumer_count << std::endl; return 0; } 。但我必须承认,带有-O3的g ++并没有优化它。

#include <iostream>
#include <thread>
#include <condition_variable>
#include <queue>
#include <cstdlib>
#include <chrono>
#include <ctime>
#include <random>
#include <atomic>

//counts every number that is added to the queue
static long long producer_count = 0;
//counts every number that is taken out of the queue
static long long consumer_count = 0;

void generateNumbers(std::queue<int> & numbers, std::condition_variable & cv, std::mutex & m, std::atomic<bool> & workdone)
{
    while(!workdone.load())
    {
        std::unique_lock<std::mutex> lk(m);
        int rndNum = rand() % 100;
        numbers.push(rndNum);
        producer_count++;
        cv.notify_one(); // Notify worker
        cv.wait(lk); // Wait for worker to complete
     }
}

void work(std::queue<int> & numbers, std::condition_variable & cv, std::mutex & m, std::atomic<bool> & workdone)
{
    while(!workdone.load() or !numbers.empty())
    {
        std::unique_lock<std::mutex> lk(m);
        cv.notify_one(); // Notify generator (placed here to avoid waiting for the lock)
        if (numbers.empty())
            cv.wait(lk); // Wait for the generator to complete
        if (numbers.empty())
            continue;
        std::cout << numbers.front() << std::endl;
        numbers.pop();
        consumer_count++;
     }
}

int main() {
    std::condition_variable cv;
    std::mutex m;
    std::atomic<bool> workdone(false);
    std::queue<int> numbers;

    //start threads
    std::thread producer(generateNumbers, std::ref(numbers), std::ref(cv), std::ref(m), std::ref(workdone));
    std::thread consumer(work, std::ref(numbers), std::ref(cv), std::ref(m), std::ref(workdone));


    //wait for 3 seconds, then join the threads
    std::this_thread::sleep_for(std::chrono::seconds(1));
    workdone = true;
    cv.notify_all(); // To prevent dead-lock

    producer.join();
    consumer.join();

    //output the counters
    std::cout << producer_count << std::endl;
    std::cout << consumer_count << std::endl;

    return 0;
}

编辑:

为了避免零星的逐个错误,你可以使用它:

extension UIBezierPath {

class func arrow(from start: CGPoint, to end: CGPoint, tailWidth: CGFloat, headWidth: CGFloat, headLength: CGFloat) -> Self {
    let length = hypot(end.x - start.x, end.y - start.y)
    let tailLength = length - headLength

    func p(_ x: CGFloat, _ y: CGFloat) -> CGPoint { return CGPoint(x: x, y: y) }
    var points: [CGPoint] = [
        p(0, tailWidth / 2),
        p(tailLength, tailWidth / 2),
        p(tailLength, headWidth / 2),
        p(length, 0),
        p(tailLength, -headWidth / 2),
        p(tailLength, -tailWidth / 2),
        p(0, -tailWidth / 2)
    ]

    let cosine = (end.x - start.x) / length
    let sine = (end.y - start.y) / length
    var transform = CGAffineTransform(a: cosine, b: sine, c: -sine, d: cosine, tx: start.x, ty: start.y)        
    let path = CGMutablePath()
    path.addLines(between: points, transform: transform)
    path.closeSubpath()
    return self.init(cgPath: path)
}

}

答案 1 :(得分:2)

请注意,此代码可能无法正常运行。 workdone变量定义为常规bool 并且编译器认为可以安全地优化它是完全合法的,因为它永远不会在代码块内发生变化。

如果你有一个混蛋反应只是添加挥发性......不,那也行不通。 你需要正确地同步对workdone变量的访问,因为两个线程都正在读取,而另一个线程(ui线程)正在编写。 另一种解决方案是使用类似事件而不是简单变量的东西。

但是对你问题的解释。 两个线程都具有相同的结束转义(!workdone),但它们具有不同的持续时间,因此目前没有任何保证生产者和消费者以某种方式同步以在一段时间内以相似数量的循环运行。

相关问题