簡體   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