有效地添加到已排序的队列

时间:2016-11-25 18:11:51

标签: c++ stl comparison containers c++98

我之前有一个传入消息的双端队列,它来自客户端消息,它们在一个线程上顺序处理。

//defined and updated elsewhere
//std::deque<Message> messages;
//typedef std::deque<Message>::iterator MessageIterator
MessageIterator result = std::remove_if(processMessages, messages.begin(), messages.end());
if(result != messages.end()) messages.erase(result,messages.end());

问题是,如果我从单个客户端收到大量消息,则其他客户端似乎对用户没有响应,因为他们的消息正在等待处理。我的解决方案是将消息存储为deques的映射,并从每个消息中取出第一个并处理它。

//typedef std::map<std::string, std::deque<Message> > MessageMapIterator
//std::map<std::string, std::deque<Message> > MessageMap
unsigned int emptyCount;
do{
    emptyCount = MessageMap.size();
    for(MessageMapIterator it = MessageMap.begin(); it != MessageMap.end(); ++it){
        if(!it->second.empty()){
            Message tmpMessage = it->second.front();
            it->second.pop_front();
            if(!ProcessMessage(tmpMessage)){
                //I need to keep some of the messages.
                unfinishedMessages.push_back(tmpMessage);
            }
        }else{
            emptyCount--;
        }
    }
}while(emptyCount > 0);

此解决方案的问题在于它似乎比在单个双端队列上执行操作要慢得多。我想知道在处理消息之前将消息组合成有序的单个双端队列是否值得,如果有的话,是否有一种有效的算法来向该队列添加新消息。

例如,假设我有一个包含来自以下编号客户端的消息的队列。 1,2,3,1,2,2,2

我从1收到一条新消息,该消息应该放到第5位。但是,如果我收到来自2的消息,它应该放在最后,如果我收到来自3的消息,它应该是排在第5位。或者我可以从新客户(4)收到一条消息,消息应该放到第4位。

似乎实现Message对象之间的比较会起作用,但是比较的结果取决于队列中的当前元素,如果我需要根据队列计算信息,那么每次我都不会有效想插入其中。

1 个答案:

答案 0 :(得分:0)

在我看来,好像你想要在循环的基础上为每个客户端处理消息。

您可以使用现有的map / deque组合,通过跟踪您弹出的最后一个客户端ID,并使用upper_bound成员函数每次弹出下一个客户端来获取下一个客户端。

但是使用multimap会更容易(也希望更有效),因为您不需要维护两个容器。

#include <map>
#include <iostream>
#include <exception>

using client_id = int;
using message = int;

class MessageQueue
{
    private:
        std::multimap<client_id, message> messages;
        client_id lastId = {};

    public:
        using message_value = std::pair<client_id, message>;

        void push(client_id id, message m)
        {
            messages.insert( std::make_pair(id, m) );
        }

        message_value pop()
        {
            // Get first message of the next highest client id
            auto nextMessage = messages.upper_bound(lastId);
            if (nextMessage == messages.end())
            {
                // Nothing higher, so start from the beginning again
                nextMessage = messages.begin();
                if (nextMessage == messages.end())
                {
                    // Nothing left on the queue
                    throw std::out_of_range("No more messages");
                }
            }
            message_value result = *nextMessage;
            messages.erase(nextMessage);
            lastId = result.first;
            return result;
        }

        size_t empty() const { return messages.empty(); }
        size_t size() const { return messages.size(); }
};

int main(int argc, char* argv[])
{
    MessageQueue mq;

    auto order = 0;
    // set initial content to 1,2,3,1,2,2,2
    mq.push(1, ++order);
    mq.push(2, ++order);
    mq.push(3, ++order);
    mq.push(1, ++order);
    mq.push(2, ++order);
    mq.push(2, ++order);
    mq.push(2, ++order);

    // Now also push on one of each client as per question
    mq.push(1, ++order);
    mq.push(2, ++order);
    mq.push(3, ++order);
    mq.push(4, ++order);

    // Process all messages
    while (!mq.empty())
    {
        auto m = mq.pop();
        std::cout << "id = " << m.first << ", message = " << m.second << std::endl;
    }
    std::cout << "Done" << std::endl;
}

使用基于手动迭代器的方法替换upper_bound方法的备用类:

class MessageQueue
{
    private:
        using container = std::multimap<client_id, message>;

        container messages;
        container::iterator lastIter;
        client_id lastId = {};

    public:
        using message_value = container::value_type;

        MessageQueue() : lastIter(messages.end()) {}

        void push(client_id id, message m)
        {
            messages.insert( std::make_pair(id, m) );
        }

        message_value pop()
        {
            auto end = messages.end();
            while (lastIter != end && lastIter->first == lastId)
            {
                ++lastIter;
            }
            if (lastIter == end)
            {
                lastIter = messages.begin();
            }
            if (lastIter == end)
            {
                // Nothing left on the queue
                throw std::out_of_range("No more messages");
            }
            auto currIter = lastIter;

            ++lastIter;
            lastId = currIter->first;

            message_value result = *currIter;
            messages.erase(currIter);
            return result;
        }

        size_t empty() const { return messages.empty(); }
        size_t size() const { return messages.size(); }
};