concurrent_queue内存消耗爆炸,然后程序崩溃

时间:2013-07-23 05:56:46

标签: c++ multithreading boost queue

这是使用VS 2010 concurent队列的典型生产者/消费者模式,问题是当我运行程序时,内存消耗超过1GB然后程序崩溃,有人可以在这段代码中指出问题吗? / p>

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib> 
#include <ctime> 

#include <boost\shared_ptr.hpp>
#include <boost\thread.hpp>
#include <concurrent_queue.h>



void wait2(int milliseconds)
{
    boost::this_thread::sleep(boost::posix_time::milliseconds(milliseconds)); 
}

class CQueue
{
    Concurrency::concurrent_queue<int>  Q;

    boost::mutex                m;
    boost::condition_variable   cv;

public:

    CQueue():QValue(-1)
    {
    }

    int QRead()
    {
        while(Q.empty())
        {
            boost::unique_lock<boost::mutex> lk(m);
            cv.wait(lk);
        }

        int res;
        if(Q.try_pop(res))
        {
            QValue = res;
            return true;
        }
        return false;
    }

    void QWrite(int i)
    {
        Q.push(i);
        cv.notify_one();
    }

    int QValue;
};

CQueue myqueue;

void write()
{
    int i = 0;
    while(true)
    {
        myqueue.QWrite(++i);
    }
}


void read()
{
    while(true)
    {
        if( myqueue.QRead())
            std::cout << myqueue.QValue << std::endl;
        else
            std::cout << "failed to read" << std::endl;
    }
}
void main ()
{

    boost::thread w(write);
    boost::thread r(read);

    w.join();
    r.join();

}

2 个答案:

答案 0 :(得分:1)

我在一个简单的双核上用VS'13和Boost 1.52构建并测试了你的代码。

正如已经说过的那样,因为生产者 - 消费者设计没有定义在库存(concurrent_queue)达到给定水平时阻止生产者的阈值,生产者在队列中推送太多数据并因此导致内存增加,windows开始交换,冻结,进程可能会崩溃,如果它超过最大提交大小等....

请注意提交大小限制取决于多个因素,编译器,编译器选项,运行程序的操作系统,...

所以在下面我添加了一种方法来阻止生产者,如果队列大小达到一个阈值,如果队列大小低于阈值,消费者唤醒生产者。

通过这些更改,我们添加了一些同步,这可以限制并行性,但使用中的内存已得到控制。

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib> 
#include <ctime> 

#include "..\..\..\boost\boost\shared_ptr.hpp"
#include "..\..\..\boost\boost\thread.hpp"

#include <concurrent_queue.h>

#define STOCK_THRESHOLD 1000

void wait2(int milliseconds)
{
    boost::this_thread::sleep(boost::posix_time::milliseconds(milliseconds)); 
}

class CQueue
{
    Concurrency::concurrent_queue<int>  Q;

    boost::mutex                consumerMutex;
    boost::condition_variable   consumerCV;

    boost::mutex                producerMutex;
    boost::condition_variable   producerCV;

public:

    CQueue():QValue(-1)
    {
    }

    int QRead()
    {
        while(Q.empty())
        {
            boost::unique_lock<boost::mutex> lk(consumerMutex);
            consumerCV.wait(lk);
        }

        int res;
        if(Q.try_pop(res))
        {
            QValue = res;
            if(Q.unsafe_size() <= STOCK_THRESHOLD)
            {
                producerCV.notify_one();
            }
            return true;
        }
        return false;
    }

    void QWrite(int i)
    {
        while(Q.unsafe_size() > STOCK_THRESHOLD){
            boost::unique_lock<boost::mutex> lk(producerMutex);
            producerCV.wait_for(lk, boost::chrono::milliseconds(10));
        }
        Q.push(i);
        consumerCV.notify_one();
    }

    int QValue;
};

CQueue myqueue;

void write()
{
    int i = 0;
    while(true)
    {
        myqueue.QWrite(++i);

    }
}


void read()
{
    while(true)
    {
        if( myqueue.QRead())
            std::cout << myqueue.QValue << std::endl;
        else
            std::cout << "failed to read" << std::endl;
    }
}

void main ()
{

    boost::thread w(write);
    boost::thread r(read);

    w.join();
    r.join();

}

答案 1 :(得分:1)

代码会丢失条件变量的通知,因此您的消费者线程会睡眠时间过长,因此消耗的速度不够快。

想象一下可以想象的线程序列:

    Producer                       Consumer
--+-----------------------------+-------------------------------------------------------
1 |                             |  while(Q.empty())
2 |   Q.push(i);                |  boost::unique_lock<boost::mutex> lk(consumerMutex);
3 |   consumerCV.notify_one();  |
4 |                             |  consumerCV.wait(lk); // notification from 3 gets lost

要在consumerCV.notify_one()之前发信号通知生产者中的条件并且在Q.empty()之前检查消费者中的队列状态时,必须保持互斥锁定。

您可以通过注释掉所有互斥和条件变量调用并将消费者更改为忙等待while(Q.empty()) /* busy-wait */;来轻松检查。

如果concurrent_queue没有提供等待项目可用的功能,那么最好使用包裹在互斥锁中的非线程安全容器。因为它仍然需要一个互斥锁和一个条件变量来正确通知使用无锁或无等待容器所获得的好处就会丢失。

另外,因为生产者只生产++i,但消费者通过打印每个值做了更多的事情,消费者不可能跟上生产者,导致队列的积累和最终记忆力耗尽。