Using STL queue to realize thread safe queue

Time:2020-7-10

brief introduction

Recently, a friend asked about thread safe queues. In this paper, the queue container based on STL realizes thread safe queue, which can produce and consume multithreads. At the same time, with the boost based circular_ Compared with the ring buffer implemented by buffer, the performance is slightly better (less than 5% in the experimental test). The source code is relatively simple, using STL and boost to achieve the function of discarding messages beyond the maximum length of the queue.

Preparation of experimental environment

Main CPU parameters: Intel 2.3ghz, 4 cores
Memory: 12g
Operating system: Windows 7

experimental result

If the number of threads is 0, it means that the production thread and the consumption thread are the same thread. After unified production, the calculation time starts from the first consumption.
The others are in the case of one thread production and n (n > = 1) thread consumption.
Using STL queue to realize thread safe queue
The line chart is shown in the figure. You can see that the queue performance achieved by using queue is the best, circular_ Buffer is second, and list is slightly worse.
Using STL queue to realize thread safe queue

Source code

In simplethreadqueue. H, the annotated part is circular using boost_ Buffer.
simplethreadqueue.h

#ifndef CSIMPLETHREADQUEUE_H
#define CSIMPLETHREADQUEUE_H
#include <string>
#include <queue>
#include <iostream>
#include "boost/timer.hpp"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp> 
#include <boost/circular_buffer.hpp>

using namespace std;
using namespace boost;
extern long g_nMaxCount;

template <class T>
class CSimpleThreadQueue
{
public:
    CSimpleThreadQueue():m_nSize(0),m_nConsumed(0){/*m_container.resize(g_nMaxCount);*/}
    size_t size(){return m_nSize;}
    long GetConsumed(){return m_nConsumed;}
    void EnQueue(T item)
    {
        m_mutex.lock();    
        while(m_nSize >= g_nMaxCount)
        {
            m_container.pop();
            --m_nSize;
        }
        m_container.push(item);

        //m_container.push_back(item);
        ++m_nSize;
        m_cond.notify_one();
        m_mutex.unlock();
    }
    void Dequeue(T& item)
    {
        while (true)
        {
            m_mutex.lock();
            if ( m_nSize > 0)
                break;
            m_cond.wait_for(m_mutex, boost::chrono::microseconds(1));
            m_mutex.unlock();
        }

        item = m_container.front();
        m_container.pop();
        //m_container.pop_front();
        -- m_nSize;
        ++m_nConsumed;
        m_mutex.unlock();
    }
    
private:
    std::queue<T> m_container;
    //circular_buffer<T> m_container;
    size_t m_nSize;
    boost::mutex m_mutex;
    condition_variable_any m_cond;
    long m_nConsumed;
};
#endif

main.cpp

#include "simplethreadqueue.h"
#include <boost/date_time/posix_time/posix_time.hpp>

long g_nMaxCount = 500000;//100w
bool g_bRunning = true;
CSimpleThreadQueue<string> g_queue;
boost::mutex g_mutex;

void CallbackMethod(string& strMessage)
{
    int sum = 0;
    for(int i = 0; i < 1000; ++ i)
         sum += i;
    //cout<<strMessage<<endl;
}

void ProduceMessageInit()
{
    for(long long i = 0; i < g_nMaxCount; ++ i)
        g_queue.EnQueue("Test message."/*std::to_string(i)*/);
}

void ProduceMessage()
{
    //static long long i = 0;
    while(g_bRunning)
    {
        g_queue.EnQueue("Test message."/*std::to_string(++i)*/);
    }
}

void ConsumeMessage()
{
    string strMessge;
    //static timer t;
    static boost::posix_time::ptime t1 = boost::posix_time::microsec_clock::universal_time();
    //static long nCount = 0;
    if(g_queue.size() > 0 && g_queue.GetConsumed() < g_nMaxCount)
    {
        g_queue.Dequeue(strMessge);
        //++ nCount;
    }
    else
    {
        g_mutex.lock();
        if(g_bRunning)
        {
            g_bRunning = false;
            boost::posix_time::ptime t2 = boost::posix_time::microsec_clock::universal_time();
            cout<<g_queue.GetConsumed()<<" consumed!"<<endl;
            cout<<t2 - t1 <<"s"<<endl;
        }
        g_mutex.unlock();
    }
    CallbackMethod(strMessge);
}

void ConsumeAllMessage()
{
    while(g_bRunning)
    {
        ConsumeMessage();
    }
}

int main(int argc, char* argv[])
{
    If (argc < = 1) // single thread message re consumption model
    {
        ProduceMessageInit();
        ConsumeAllMessage();
        return 0;
    }
    //Single thread production and multi thread consumption model
    ProduceMessageInit();
    
    thread_group tg;
    tg.create_thread(boost::bind(ProduceMessage));
    int nThreadCount = atoi(argv[1]);
    if(nThreadCount <= 0)
        return -1;
    for(int i = 0; i < nThreadCount ; ++i)
        tg.create_thread(boost::bind(ConsumeAllMessage));
    tg.join_all();
    return 0;
}

Recommended Today

ASP.NET Example of core MVC getting the parameters of the request

preface An HTTP request is a standard IO operation. The request is I, which is the input; the responsive o is the output. Any web development framework is actually doing these two things Accept the request and parse to get the parameters Render according to the parameters and output the response content So let’s learn […]