Simple producer/consumer queue

Preamble
A very simple example of a single producer/consumer using locks and move operations.

Code

template<typename T>
class ProdCon
{
public:
	void push(T&& w_)
	{
		scoped_lock<mutex> lock{_mtx};
		if (_q.empty())
		{
			_cv.notify_one();
		}
		_q.emplace(move(w_));
	}

	T pop()
	{
		unique_lock<mutex> lock{_mtx};
		_cv.wait(lock, [this](){ return !_q.empty(); } );
		auto& w = _q.front();
		_q.pop();
		return move(w);
	}
private:
	queue<T> _q;
	mutex _mtx;
	condition_variable _cv;
};

Points to note are:

  • Objects of type T are only ever moved.
  • The queue is only signalled when it is empty for efficiency reasons.
  • Full code

    #include <iostream>
    #include <queue>
    #include <mutex>
    #include <thread>
    #include <future>
    using namespace std;
    
    template<typename T>
    class ProdCon
    {
    public:
    	void push(T&& w_)
    	{
    		scoped_lock<mutex> lock{_mtx};
    		if (_q.empty())
    		{
    			_cv.notify_one();
    		}
    		_q.emplace(move(w_));
    	}
    
    	T pop()
    	{
    		unique_lock<mutex> lock{_mtx};
    		_cv.wait(lock, [this](){ return !_q.empty(); } );
    		auto& w = _q.front();
    		_q.pop();
    		return move(w);
    	}
    private:
    	queue<T> _q;
    	mutex _mtx;
    	condition_variable _cv;
    };
    
    //-------------------TestCode-----------------------
    
    static int widgetNum = 1;
    
    class Widget
    {
    public:
    	Widget() : _widgetNum{widgetNum++} {}
    	Widget(const Widget&) = delete;
    	Widget& operator=(const Widget&) = delete;
    	Widget(Widget&&) = default;
    	Widget& operator=(Widget&&) = default;
    
    	int num() { return _widgetNum; }
    
    private:
    	int _widgetNum;
    };
    
    void staticTest()
    {
    	cout << "staticTest" << endl;
    
    	widgetNum = 1;
    	Widget w1, w2, w3, w4;
    
    	ProdCon<Widget> pc;
    
    	pc.push(move(w1));
    	pc.push(move(w2));
    
    	cout << pc.pop().num() << endl;
    	cout << pc.pop().num() << endl;
    
    	pc.push(move(w3));
    
    	cout << pc.pop().num() << endl;
    
    	pc.push(move(w4));
    
    	cout << pc.pop().num() << endl;
    }
    
    
    void dynamicTest()
    {
    	cout << "dynamicTest" << endl;
    
    	widgetNum = 1;
    	Widget w1, w2, w3, w4;
    
    	ProdCon<Widget> pc;
    
    	auto prod = [&](){ pc.push(move(w1)); pc.push(move(w2)); pc.push(move(w3)); pc.push(move(w4)); };
    	auto con = [&](){ for(int i = 1; i<=4; ++i) {cout << pc.pop().num() << endl;}; };
    
    	auto fut = async(launch::async, con);
    	async(launch::async, prod);
    
    	//wait for consumer
    	fut.get();
    }
    
    int main()
    {
    	staticTest();
    	dynamicTest();
    
    	return 0;
    }
    

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.