Communication between threads of C + + nine Yin Scripture (message queue)

Message queuing is a common way of communication between threads, which is often used to solve the communication between threads of the classic model producer consumer model.

This paper will end the implementation of message queue based on C + + standard library, which can support any parameter type and the number of task parameters.

In order to facilitate the implementation of subsequent thread pools and asynchronous queues, the common base classes are extracted here.

class QueueObject : public noncopyable
{
public:
	QueueObject() :m_bStop(false), m_nCapacity(MAX_QUEUE_CAPACITY)
	{
	}

	virtual ~QueueObject()
	{
	}

	void Stop()
	{
		m_bStop.store(true);
		m_condPop.notify_all(); // Wake up all threads to execute
	}

	//Set maximum capacity
	void SetMaxCapacity(int nMax)
	{
		m_nCapacity = nMax;
	}
	//Get the number of queue tasks
	virtual size_t GetTaskNum() = 0;


	bool IsStop()
	{
		return m_bStop;
	}

protected:
	int m_nCapacity = 0;                     //Maximum capacity of queue
	std::condition_variable_any m_condPush;  //Write condition quantity
	std::condition_variable_any m_condPop;   //Read condition quantity
	std::mutex m_mu;   //mutex  

	// Close submission or not
	std::atomic<bool> m_bStop;

};

Message queuing implementation

template<typename T, typename... ARGS>
class CMsgQueue : public QueueObject
{
public:
	using QueueObject::QueueObject;
	void Push(T val, const ARGS... args)
	{
		while (m_dataQueue.size() == m_nCapacity)         //Queue full
		{
			m_condPush.wait(m_mu);                         //Wait, will unlock temporarily
		}

		m_dataQueue.emplace(std::make_tuple(val, args...));

		m_condPop.notify_one(); // Wake up a thread to execute
	}

	//Get parameter values in batch
	bool Pop(std::tuple<T, ARGS...>& value, int waitTime = -1)
	{
		std::unique_lock<std::mutex> lock(m_mu);
		if (waitTime < 0)
		{
			this->m_condPop.wait(lock,
				[this] {
				return  !this->m_dataQueue.empty();
			}); // wait until there is a task
		}
		else
		{
			auto status = m_condPop.wait_for(lock, std::chrono::seconds(waitTime), [this] {
				return  !this->m_dataQueue.empty();
			});
			if (!status )
			{
				return false;
			}
		}

		value = std::move(this->m_dataQueue.front()); // Take a task
		this->m_dataQueue.pop();

		//Notification writer thread
		m_condPush.notify_one();

		return true;
	}

  	
	bool Pop( T& value, ARGS&... args, int waitTime = -1)
	{
		std::tuple<T,ARGS...> tupVal;
		if (Pop(tupVal, waitTime))
		{
			FetchParam<0>(tupVal, value, args...);
			return true;
		}
		return false;
	}


    template<int NUM, typename P, typename...PARMS>
    void FetchParam(std::tuple<T,ARGS...>& tupVal,  P& p, PARMS&... params)
    {
        p = std::get<NUM>(tupVal);
        FetchParam<NUM+1>(tupVal,  params...);
    }

    template<int NUM, typename P>
    void FetchParam(std::tuple<T,ARGS...>& tupVal, P& p)
    {
        p = std::get<NUM>(tupVal);
    } 

	//Get the number of queue tasks
	virtual size_t GetTaskNum()
	{
		return m_dataQueue.size();
	}

private:
	std::queue<std::tuple<T, ARGS...>> m_dataQueue;
};

Test:

int main()
{
    CMsgQueue<std::string, int, int> mq;

    mq.Push("test", 10,20);
    mq.Push("test2", 100,200);

    std::string val;
    int num1, num2;
    mq.Pop(val, num1, num2);
    std::cout << val << "   " << num1 << "  " << num2 << std::endl;
    mq.Pop( val, num1, num2);
    std::cout << val << "   " << num1 << "  " << num2 << std::endl;
    return 0;
}

Tags: Programming

Posted on Mon, 01 Jun 2020 10:21:37 -0400 by gowni