繁体   English   中英

priority_queue具有动态优先级

[英]priority_queue with dynamic priorities

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

例如,接受查询的结果(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;
};

您想要实现多少个离散优先级值? 如果它们的数量很小(比如说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移到队列#25如果队列是std :: list并且使用了list::splice()则为O(1)。

如果排序条件发生更改,则std::priority_queue无法重新组织自身。 你自己要管理它。

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

有两种情况:如果你重新排序优先级比删除元素更频繁(听起来不是这种情况),只需保持未排序并使用min_element找到要在nedeed时删除的项目。

否则可能更好的方法是使用Kristo的方法并维护自己的堆。 当你重新安排通话make_heap ,添加使用push_heap ,并让高层项目使用pop_heap

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

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

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

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

首先是对您的代码的一些评论:

  1. 您无法保证每次移除时只会为每个对象调用一次operator <(可以在top和pop,或者push,或者......)调用它。
  2. 您可以在运算符函数中增加本地副本的优先级,而不是队列中的副本
  3. 这里不需要使用友元函数来声明运算符<。

我写了一个示例,将priority_queue覆盖到您想要的特定队列,我希望您可以从这里继续。 行为应该在队列中实现,而不是在Query类中实现,这只应提供必要的访问器以允许此行为。 Query类应该不了解Queue。

基本上它会复制正常队列的大小和空白,并创建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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM