priority_queue具有动态优先级

时间:2010-05-27 13:08:22

标签: c++ priority-queue

我有一个服务器应用程序,它接受命令查询并执行它们。如果查询太多,则应排队,如果执行了其他一些查询,则也应执行排队查询。由于我想传递具有不同优先级的查询,我认为使用priority_queue将是最佳选择。

e.g。接受查询的结果(a)达到了极限,新的查询将存储在队列中。如果来自(a)的某些查询被执行,则所有查询的优先级都为1(最低),程序将从队列中选择具有最高优先级的查询并执行它。仍然没问题。现在有人发送优先级为5的查询,并将其添加到队列中。由于这是具有最高优先级的查询,因此只要正在运行的查询不再达到限制,应用程序就会执行此查询。最糟糕的情况可能是500个优先级为1的查询已排队但由于有人总是发送优先级为5的查询,因此这些500个查询将排队等待一段时间。为了防止我想要增加优先级低于具有更高优先级的查询的所有查询的优先级,在此示例中优先级低于5.因此,如果优先级为5的查询被拔出队列的所有其他查询的优先级< 5应该增加0.2。这样,即使可能有100个具有更高优先级的查询,具有低优先级的查询也不会排队。

我真的希望有人可以帮我解决问题:

由于我的查询包含一个对象,我认为这样的东西可能会起作用:

class Query {
public:
    Query( std::string p_stQuery ) : stQuery( p_stQuery ) {};
    std::string getQuery() const {return stQuery;};
    void increasePriority( const float fIncrease ) {fPriority += fIncrease;};

    friend bool operator < ( const Query& PriorityFirst, const Query& PriorityNext ) {
        if( PriorityFirst.fPriority < PriorityNext.fPriority ) {
            if( PriorityFirst.fStartPriority < PriorityNext.fStartPriority ) {
                Query qTemp = PriorityFirst;
                qTemp.increasePriority( INCREASE_RATE );
            }

            return true;
        } else {
            return false;
        }
    };

private:
    static const float INCREASE_RATE = 0.2;
    float fPriority; // current priority
    float fStartPriority; // initialised priority
    std::string stQuery;
};

6 个答案:

答案 0 :(得分:3)

您想要实现多少个离散优先级值?如果它们的数量很小(比如说256级),那么代替单个优先级队列,就有256个简单队列更有意义(这是在某些操作系统中实现优先级进程调度程序的方式)。最初以优先级1发送的事件将放在队列#1上。然后有人发送prio = 25事件,并将其放在队列#25上。读取器从最高非空队列(在本例中为#25)中读取事件,并将25以下的所有非空队列上的事件向上移动一个槽(整个队列#1移动到队列#2等) 。我很确定这可以在O(k)(其中k是优先级的数量)中完成,无论是使用std :: move还是使用std :: swap或者使用list :: splice。

将队列#1移动到队列#2先前占用的位置应为带移动或交换的O(1),如果prio = 25队列的剩余部分不为空,则将所有元素从队列#24移入如果队列是std :: list并且使用list::splice(),队列#25是O(1)。

答案 1 :(得分:1)

如果您的排序条件发生变化,则std::priority_queue无法重新组织自己。你自己要管理它。

我建议只使用std::vector和两种方法之一来维护它。

有两种情况:如果您重新选择优先次序的次数比删除元素的频率要高(听起来不是这种情况),只需将其保持未分类状态,并使用min_element查找要求时删除的项目。

否则可能更好的方法是使用Kristo的方法并维护自己的堆。重新设置调用make_heap的优先级时,要添加使用push_heap,并使用pop_heap来获取热门项目。

答案 2 :(得分:0)

如果您需要修改优先级,则不能使用priority_queue,因为没有接口可以访问所有元素。最好使用std::vectorstd::make_heap

答案 3 :(得分:0)

ACE框架可能会提供一些可以帮助您的东西。请参阅课程ACE_Dynamic_Message_QueueACE_Laxity_Message_Strategy。 我还没有使用过这个特定的课程,所以我不能举个例子。 但这个想法如下:

  • 您有一个由两个线程共享的消息队列
  • 在生产者线程中,填写消息队列
  • 消息队列将根据策略在正确的位置插入新消息。
  • 在接收器线程中,您只需读取队列中最顶层的项目。

答案 4 :(得分:0)

我会使用std :: deque并自己构建其余的(如果你只是使用C ++而没有可能有帮助的外部库)。 make_heap的其他建议(std :: priority_queue使用的)的问题在于它不稳定。在这种情况下缺乏稳定性意味着在优先级范围内不能保证排序,并且可能存在饥饿。

答案 5 :(得分:0)

首先对您的代码提出一些意见:

  1. 您不能保证运营商&lt;只会在每次删除时为每个对象调用一次(可以在top和pop,或者push,或者......)调用它。
  2. 您可以在操作员功能中增加本地副本的优先级,而不是队列中的副本
  3. 此处无需使用友元函数来声明运算符&lt;。
  4. 我写了一个示例,将priority_queue覆盖到您想要的特定队列,我希望您可以从这里继续。行为应该在队列中实现,而不是在Query类中实现,这只应提供必要的访问器以允许此行为。 Query类应该不了解队列。

    基本上它会复制普通队列的大小和空白,并创建2个新方法来插入和获取查询(push,pop和top被禁用)。插入只调用push,get调用top,pop并使用本地容器上的for_each调用更新所有优先级。最后,提供了一个显示功能的小程序。

    此外,它基于pop和push中的堆管理。据我所知,这将正常工作,因为每个元素的线性变化,在推动之间顺序不会改变;)。

    #include <algorithm>
    #include <queue>
    #include <iostream>
    
    class Query
    {
    public:
        Query( std::string p_stQuery, double priority = 1.0) : stQuery( p_stQuery ) , fPriority(priority), fStartPriority(priority) {};
        std::string getQuery() const
        {
            return stQuery;
        };
        void conditionalIncrease( const Query& otherQuery )
        {
            if (fStartPriority < otherQuery.fStartPriority) fPriority += INCREASE_RATE;
        }
    
        bool operator < ( const Query& rhs )  const
        {
            return fPriority < rhs.fPriority;
        }
    
        double getPriority() const
        {
            return fPriority;
        }
    private:
        static const double INCREASE_RATE = 0.2;
        double fPriority; // current priority
        double fStartPriority; // initialised priority
        std::string stQuery;
    };
    
    class QueryIncreaser
    {
    private:
        Query base;
    public:
        QueryIncreaser(const Query& q) : base(q) {}
        void operator() (Query& q)
        {
            q.conditionalIncrease(base);
        }
    };
    
    class QueryQueue : private std::priority_queue<Query>
    {
    public:
        void insertQuery(const Query& q)
        {
            push(q);
        }
        Query getQuery()
        {
            Query q = top();
            pop();
            QueryIncreaser comp(q);
            for_each(c.begin(), c.end(), comp);
            return q;
        }
        bool empty() const
        {
            return std::priority_queue<Query>::empty();
        }
        size_t size() const
        {
            return std::priority_queue<Query>::size();
        }
    };
    
    int main ()
    {
        Query a("hello"), b("world",2.);
        QueryQueue myQueue;
        myQueue.insertQuery(a);
        myQueue.insertQuery(b);
        while (!myQueue.empty())
        {
            std::cout << myQueue.getQuery().getPriority() << std::endl;
        }
        return 0;
    }