制片人&消费者线程使用条件变量

时间:2014-12-09 10:42:22

标签: c++ multithreading boost

问题是生产者线程没有等待,我希望生产者线程等待消费者线程,直到消费者完成处理4000个项目,一旦完成,它将通知生产者线程,然后生产者线程可以继续下一批。

我在这里做错了什么?

另一个问题是,批次中并不总是有4000个项目。 ESP。如果你采取最后一批。在这种情况下,Produce线程应通知Consumer线程完成<deque>

中剩余项目的处理
#include <windows.h>

#include <string>
#include <iostream>
#include <deque>
#include <chrono>
#include <thread>

#include <boost/scoped_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>

using namespace std;
using namespace boost;

boost::mutex ResponseMutex;
boost::condition_variable qFull, qEmpty;

class A
{
private:
    boost::thread*                myProducerThread;
    boost::thread*                myConsumerThread;

public:
    A()
    {
        myProducerThread = nullptr;
        myConsumerThread = nullptr;
    }

    void RunThreads();
    void RunProducer();
    void RunConsumer();

    void ProducerWait();
    void ConsumerWait();

    struct Record
    {
        char response[128];

        Record(const char* response)
        {
            memset(this->response,0,sizeof(this->response));
            strcpy(this->response, response);
        }

        ~Record()
        {
        }

        Record& operator= (const Record& cmd)
        {
            if(this == &cmd)       // Same object?
            {
                return *this;
            }

            memset(this->response,0,sizeof(this->response));
            strcpy(this->response, cmd.response);
            return *this;
        }
    };

    typedef deque<Record> RecordsQueue;
};

A::RecordsQueue Records;

void A::RunThreads()
{
    myProducerThread = new boost::thread(boost::bind(&A::RunProducer, this));
    HANDLE threadHandle1 = myProducerThread->native_handle();
    SetThreadPriority(threadHandle1, THREAD_PRIORITY_NORMAL);

    myConsumerThread = new boost::thread(boost::bind(&A::RunConsumer, this));
    HANDLE threadHandle2 = myConsumerThread->native_handle();
    SetThreadPriority(threadHandle2, THREAD_PRIORITY_NORMAL);

    myProducerThread->join();
    myConsumerThread->join();
}

void A::ProducerWait()
{
    boost::mutex::scoped_lock lock(ResponseMutex);

    while(!Records.empty())
    {
        qEmpty.wait(lock);
    }
}

void A::ConsumerWait()
{
    boost::mutex::scoped_lock lock(ResponseMutex);

    while(Records.size() <= 4000)
    {
        qFull.wait(lock);
    }
}

void A::RunProducer()
{
    int i = 0;

    while(true)
    {
        ProducerWait();

        vector<string> responses;
        responses.push_back(to_string(i));
        cout<< "Added: " << to_string(i) << endl;

        i++;

        qFull.notify_one();
    }
}

void A::RunConsumer()
{
    while(true)
    {
        ConsumerWait();

        Record res = Records.front();
        cout<< "Processed: " << res.response << endl;
        Records.pop_front();

        qEmpty.notify_one();
    }
}

int main()
{
    A a;
    a.RunThreads();
}

1 个答案:

答案 0 :(得分:2)

while (true) {

    std::vector<std::string> responses;
    responses.push_back(std::to_string(i));

    qFull.notify_one();
}

您不会分享回复。消费者将如何看待响应向量?它将在每次循环迭代开始时为空。

另外

  • 不要无偿使用平台相关代码
  • 不要无偿地使用动态分配(你正在泄漏资源)
  • 不要将全局变量与类本地职责混合

接下来:您的条件是这样的,即在发布1个工作项(记录)后,您将等到消费者消耗4000个。我不知道它是如何工作的。

为什么不在生产者方面累积4000件物品,然后将其交给消费者?这样,您实际上可以从线程中受益。想一想:你有两个线程:一个是等待创建4000个作业,另一个是等待第一个完全清空队列。你在这里得到的是带有很多噪音和不必要的锁争用的美化顺序代码。

加成

在这里使用我的水晶球是一个修复上述大部分内容的版本(包括缺少锁定,考虑虚假唤醒等)。

  

您仍然需要修复strcpy 的不安全使用 !!!

#include <string>
#include <iostream>
#include <deque>
#include <chrono>
#include <thread>

#include <boost/scoped_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>

static constexpr auto MIN_QUEUE_WORK = 10;
static constexpr auto MAX_QUEUE_WORK = 40; // 4000;

class A {
  private:
    boost::thread myProducerThread;
    boost::thread myConsumerThread;

    boost::mutex mutex_;
    boost::condition_variable pushed_, popped_;

    struct Record {
        char response[128];

        Record(const char *response) {
            memset(this->response, 0, sizeof(this->response));
            strcpy(this->response, response);
        }

        ~Record() {}

        Record &operator=(const Record &cmd) {
            if (this == &cmd) // Same object?
            {
                return *this;
            }

            memset(this->response, 0, sizeof(this->response));
            strcpy(this->response, cmd.response);
            return *this;
        }
    };

    typedef std::deque<Record> RecordsQueue;
    RecordsQueue queue_;

  public:
    void RunThreads();
    void RunProducer();
    void RunConsumer();
};

void A::RunThreads() {
    myProducerThread = boost::thread(&A::RunProducer, this);
    myConsumerThread = boost::thread(&A::RunConsumer, this);

    myProducerThread.join();
    myConsumerThread.join();
}

void A::RunProducer() {
    int i = 0;

    while (i<1000) {
        boost::mutex::scoped_lock lock(mutex_);
        popped_.wait(lock, [this] { return queue_.size()<MAX_QUEUE_WORK; });

        queue_.push_back(Record { std::to_string(i).c_str() });
        std::cout << "Added: " << std::to_string(i) << " size: " << queue_.size() << std::endl;
        i++;
        pushed_.notify_one();
    }
}

void A::RunConsumer() {
    while (true) {
        boost::mutex::scoped_lock lock(mutex_);
        pushed_.wait(lock, [this]{return queue_.size()>MIN_QUEUE_WORK;});

        Record res = queue_.front();
        std::cout << "Processed: " << res.response << std::endl;
        queue_.pop_front();

        popped_.notify_one();
    }
}

int main() {
    A a;
    a.RunThreads();
}