简体   繁体   English

priority_queue具有动态优先级

[英]priority_queue with dynamic priorities

I have a server application which accepts incomming queries and executes them. 我有一个服务器应用程序,它接受命令查询并执行它们。 If there are too many queries they should be queued and if some of the other queries got executed the queued queries should be executed as well. 如果查询太多,则应排队,如果执行了其他一些查询,则也应执行排队查询。 Since I want to pass queries with different priorities I think using a priority_queue would be the best choice. 由于我想传递具有不同优先级的查询,我认为使用priority_queue将是最佳选择。

eg The amout of the axcepting queries (a) hit the limt and new queries will be stored in the queue. 例如,接受查询的结果(a)命中了限制,新的查询将被存储在队列中。 All queries have a priority of 1 (lowest) if some of the queries from (a) get executed the programm will pick the query with the highest priority out of the queue and execute it. 如果来自(a)的某些查询被执行,则所有查询的优先级都为1(最低),程序将从队列中选择具有最高优先级的查询并执行它。 Still no problem. 仍然没问题。 Now someone is sending a query with a priority of 5 which gets added to the queue. 现在有人发送优先级为5的查询,并将其添加到队列中。 Since this is the query with the highest priority the application will execute this query as soon as the running queries no longer hit the limit. 由于这是具有最高优先级的查询,因此只要正在运行的查询不再达到限制,应用程序就会执行此查询。 There might be the worst case that 500 queries with a priority of 1 are queued but wont be executed since someone is always sending queries with a priority of 5 hence these 500 queries will be queued for a looooong time. 最糟糕的情况可能是500个优先级为1的查询已排队但由于有人总是发送优先级为5的查询,因此这些500个查询将排队等待一段时间。 In order to prevent that I want to increase the prioritiy of all queries which have a lower priority than the query with the higher priority, in this example which have a priority lower than 5. So if the query with a priority of 5 gets pulled out of the queue all other queries with a priority < 5 should be increased by 0.2. 为了防止我想要增加优先级低于具有更高优先级的查询的所有查询的优先级,在此示例中优先级低于5.因此,如果优先级为5的查询被拔出在队列中,优先级<5的所有其他查询应该增加0.2。 This way queries with a low priority wont be queued for ever even if there might be 100 queries with a higher priority. 这样,即使可能有100个具有更高优先级的查询,具有低优先级的查询也不会排队等待。

I really hope someone can help me to solve the problem with the priorities: 我真的希望有人可以帮助我解决优先问题:

Since my queries consist of an object I thought something like this might work: 由于我的查询包含一个对象,我认为这样的东西可能会起作用:

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;
};

How many discrete priority values do you want to implement? 您想要实现多少个离散优先级值? If their number is small (say, 256 levels), then instead of a single priority queue it makes more sense to have 256 simple queues (this is how priority process schedulers are implemented in some OSes). 如果它们的数量很小(比如说256级),那么代替单个优先级队列,就有256个简单队列更有意义(这是在某些操作系统中实现优先级进程调度程序的方式)。 Initially your events sent with priority 1 are placed on queue #1. 最初以优先级1发送的事件将放在队列#1上。 Then someone sends a prio=25 event, and it is placed on queue #25. 然后有人发送prio = 25事件,并将其放在队列#25上。 The reader reads the event from the highest non-empty queue (in this case #25) and the events on all non-empty queues under 25 are moved up a slot (the entire queue #1 is moved to queue #2, etc). 读取器从最高非空队列(在本例中为#25)中读取事件,并将25以下的所有非空队列上的事件向上移动一个槽(整个队列#1移动到队列#2等) 。 I am pretty sure this could all be done in O(k) (where k is the number of priority levels), either with std::move or with std::swap or with list::splice. 我很确定这可以在O(k)(其中k是优先级的数量)中完成,或者使用std :: move或者使用std :: swap或者使用list :: splice。

Moving queue #1 to the position earlier taken by queue #2 should be O(1) with move or swap, and if the remainder of prio=25 queue is not empty, then moving all elements up from queue #24 into queue #25 is O(1) if the queues are std::list's and list::splice() is used. 将队列#1移动到队列#2先前占用的位置应为带移动或交换的O(1),如果prio = 25队列的剩余部分不为空,则将所有元素从队列#24移到队列#25如果队列是std :: list并且使用了list::splice()则为O(1)。

A std::priority_queue doesn't have any way to reorganize itself if your sort criteria changes. 如果排序条件发生更改,则std::priority_queue无法重新组织自身。 You'll nede to manage it yourself. 你自己要管理它。

I would suggest just using a std::vector and one of two approaches to maintain it. 我建议只使用std::vector和两种方法之一来维护它。

There are two cases: If you're reprioritizing rather more often than you remove elements (which sounds like is not the case), just keep it unsorted and use min_element to find the item to remove when nedeed. 有两种情况:如果你重新排序优先级比删除元素更频繁(听起来不是这种情况),只需保持未排序并使用min_element找到要在nedeed时删除的项目。

Otherwise probably better is to use Kristo's approach and maintain your own heap. 否则可能更好的方法是使用Kristo的方法并维护自己的堆。 When you reprioritize call make_heap , to add use push_heap , and to get the top item use pop_heap . 当你重新安排通话make_heap ,添加使用push_heap ,并让高层项目使用pop_heap

If you need to modify the priorities, then you can't use a priority_queue because there's no interface to access all the elements. 如果需要修改优先级,则不能使用priority_queue,因为没有接口可以访问所有元素。 You're better off with std::vector and std::make_heap . 你最好使用std::vectorstd::make_heap

The ACE framework probably provides something that could help you. ACE框架可能提供可以帮助您的东西。 See classes ACE_Dynamic_Message_Queue and ACE_Laxity_Message_Strategy . 请参阅ACE_Dynamic_Message_QueueACE_Laxity_Message_Strategy类。 I haven't worked with this particular classes yet, so I could not give you an example. 我还没有使用过这个特定的课程,所以我不能举个例子。 But the idea is as follows: 但这个想法如下:

  • You have a message queue shared by two threads 您有两个线程共享的消息队列
  • In the producer thread, you fill the message queue 在生产者线程中,填充消息队列
  • The message queue will insert new messages at the correct place according to the strategy. 消息队列将根据策略在正确的位置插入新消息。
  • In the receiver thread you just read the topmost item from the queue. 在接收器线程中,您只需从队列中读取最顶层的项目。

I would go with a std::deque and build the rest yourself(if you are just using C++ with no external libs that might help). 我会使用std :: deque并自己构建其余的(如果你只是使用C ++而没有可能有帮助的外部库)。 The problem with the other suggestions of make_heap (which is what std::priority_queue uses) is that it isn't stable. make_heap的其他建议(std :: priority_queue使用的)的问题在于它不稳定。 Lack of stability in this case means that ordering isn't guarantied within a priority and starvation is possible. 在这种情况下缺乏稳定性意味着在优先级内不能保证排序,并且可能存在饥饿。

First of all some comments on your code: 首先是对您的代码的一些评论:

  1. You cannot guarantee that operator < will only be called once for each object every removal (it can be called both at top and pop, or on push, or ...). 您无法保证每次移除时只会为每个对象调用一次operator <(可以在top和pop,或者push,或者......)调用它。
  2. You increase the priority of a local copy in the operator function, not the copy in the queue 您可以在运算符函数中增加本地副本的优先级,而不是队列中的副本
  3. There is no need to use friend functions here to declare an operator<. 这里不需要使用友元函数来声明运算符<。

I wrote an example that overrides the priority_queue to the specific queue you want, I hope you can continue from here. 我写了一个示例,将priority_queue覆盖到您想要的特定队列,我希望您可以从这里继续。 The behaviour should be implemented in the queue, not in the Query class at all, this only should provide the necessary accessors to allow this behaviour. 行为应该在队列中实现,而不是在Query类中实现,这只应提供必要的访问器以允许此行为。 The Query class should have no knowledge about the Queue. Query类应该不了解Queue。

Basically it copies the size and empty of the normal queue, and creates 2 new methods to insert and get queries (push, pop and top are disabled). 基本上它会复制正常队列的大小和空白,并创建2个新方法来插入和获取查询(push,pop和top被禁用)。 Insert just calls push, get calls both top, pop and updates all priorities using a for_each call on the local container. 插入只调用push,get调用top,pop并使用本地容器上的for_each调用更新所有优先级。 Finally a small program is provided showing functionality. 最后,提供了一个显示功能的小程序。

Also, it is based on the heap managing in pop and push. 此外,它基于pop和push中的堆管理。 This will work correctly as far as I know because of the linear change on every element, the order does not change between pushes ;). 据我所知,这将正常工作,因为每个元素的线性变化,顺序在推动之间不会改变;)。

#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