簡體   English   中英

.NET 4.0 中的並發優先隊列

[英]Concurrent Priority Queue in .NET 4.0

.NET 4.0 中似乎有很多與可能依賴於並發優先級隊列的並發相關的改進。 框架內是否有可重用的體面優先級隊列實現?

msdn 上的“使用 .NET 框架進行並行編程的示例”中有一個實現。 請參閱ParallelExtensionsExtras

文件ConcurrentPriorityQueue.cs源代碼的直接鏈接

您可能需要自己滾動。 一種相對簡單的方法是擁有一系列常規隊列,優先級會降低。

基本上,您將插入隊列以獲得適當的優先級。 然后,在消費者方面,您將按照優先級從高到低的順序排列列表,檢查隊列是否為非空,如果是,則消耗一個條目。

也許您可以使用我自己的 PriorityQueue 實現。 它實現了比通常的 push/pop/peek 多得多的功能,每當我發現需要它時就會實現這些功能。 它還具有並發鎖。

非常感謝對代碼的評論:)

public class PriorityQueue<T> where T : class
{
    private readonly object lockObject = new object();
    private readonly SortedList<int, Queue<T>> list = new SortedList<int, Queue<T>>();

    public int Count
    {
        get
        {
            lock (this.lockObject)
            {
                return list.Sum(keyValuePair => keyValuePair.Value.Count);
            }
        }
    }

    public void Push(int priority, T item)
    {
        lock (this.lockObject)
        {
            if (!this.list.ContainsKey(priority))
                this.list.Add(priority, new Queue<T>());
            this.list[priority].Enqueue(item);
        }
    }
    public T Pop()
    {
        lock (this.lockObject)
        {
            if (this.list.Count > 0)
            {
                T obj = this.list.First().Value.Dequeue();
                if (this.list.First().Value.Count == 0)
                    this.list.Remove(this.list.First().Key);
                return obj;
            }
        }
        return null;
    }
    public T PopPriority(int priority)
    {
        lock (this.lockObject)
        {
            if (this.list.ContainsKey(priority))
            {
                T obj = this.list[priority].Dequeue();
                if (this.list[priority].Count == 0)
                    this.list.Remove(priority);
                return obj;
            }
        }
        return null;
    }
    public IEnumerable<T> PopAllPriority(int priority)
    {
        List<T> ret = new List<T>();
        lock(this.lockObject)
        {
            if (this.list.ContainsKey(priority))
            {
                while(this.list.ContainsKey(priority) && this.list[priority].Count > 0)
                    ret.Add(PopPriority(priority));
                return ret;
            }
        }
        return ret;
    }
    public T Peek()
    {
        lock (this.lockObject)
        {
            if (this.list.Count > 0)
                return this.list.First().Value.Peek();
        }
        return null;
    }
    public IEnumerable<T> PeekAll()
    {
        List<T> ret = new List<T>();
        lock (this.lockObject)
        {
            foreach (KeyValuePair<int, Queue<T>> keyValuePair in list)
                ret.AddRange(keyValuePair.Value.AsEnumerable());
        }
        return ret;
    }
    public IEnumerable<T> PopAll()
    {
        List<T> ret = new List<T>();
        lock (this.lockObject)
        {
            while (this.list.Count > 0)
                ret.Add(Pop());
        }
        return ret;
    }
}

好吧,7 年過去了,但為了后代,我想用我的實現來回答。

文檔:可選等待簡單易用的並發優先隊列

源代碼:github

nuget 包

  • 無鎖,
  • 高並發,
  • 存儲項目類型中的泛型,
  • 優先級類型的泛型,但受限於 .net 枚舉表示的優先級,強類型優先級,
  • 在構建期間明確定義優先級的降序,
  • 能夠檢測項目數和每個優先級項目數,
  • 出隊的能力 - 優先級的降序,
  • 能夠覆蓋出隊優先級,
  • 潛在的等待,
  • 潛在的基於優先級的等待,

由於所有當前的答案都已過時或未提供可行的解決方案,因此MSDN 上有一個可用的實現。 請注意,在此實現中優先處理較低的優先級。

檢查.NET Framework 4 中的線程安全集合及其性能特征,但 AFAIK 沒有准備好使用優先級隊列。 所有新的線程安全集合都不會維護順序,但您可以在它們之上創建自己的集合。 檢查@Steven 的方式。

選項:

1)如果您的隊列永遠不會變大,請使用堆並為每次插入和刪除鎖定整個結構。

2)如果你的隊列會變大,你可以使用這樣的算法:

http://www.research.ibm.com/people/m/michael/ipl-1996.pdf

該算法支持一次僅對樹的一部分進行細粒度鎖定,從而允許多個線程同時使用堆結構,而不會冒損壞或死鎖的風險。 您必須進行基准測試,以查看額外鎖定和解鎖操作的開銷是否比鎖定整個堆的爭用成本更高。

3)如果您的目標是完全避免鎖定,則上面鏈接中提到的另一種算法建議使用 FIFO 請求隊列(無需鎖定即可輕松實現),以及一個單獨的線程,它是唯一接觸堆的線程。 您必須測量以查看使用同步對象在線程之間切換焦點的開銷與普通直接鎖定的開銷相比如何。

在您開始之前,有必要了解一下使用鎖定的簡單實現中的命中有多么糟糕。 它可能不是最有效的實現,但如果它的執行速度仍然比您需要的快幾個數量級,那么易於維護(也就是說,任何人,包括您現在一年都可以,能夠簡單地查看代碼並了解它的作用)可能會超過花在排隊機制上的 CPU 時間的一小部分。

希望這有幫助:-)

最近,我正在創建一個狀態機,我需要在其中添加時間戳事件。 不僅僅是一個簡單的時鍾滴答聲,我需要帶有自己 ID 的定時事件,以便我可以將一個事件與另一個事件區分開來。

研究這個問題使我產生了使用優先隊列的想法。 我可以按任何順序將定時事件及其信息排入隊列; 優先級隊列將負責正確排序事件。 計時器會定期檢查優先級隊列,以查看是否到了觸發隊列頭部事件的時間。 如果是這樣,它將使事件出隊列並調用與其關聯的委托。 這種方法正是我正在尋找的。

在 CodeProject 中搜索

https://www.codeproject.com/Articles/13295/A-Priority-Queue-in-C

我發現已經寫了一個優先級隊列[^] 類。 但是,我突然想到,我可以使用我的老朋友跳過列表輕松編寫自己的內容。 這樣做的好處是出隊操作只需要 O(1) 時間,而入隊操作平均仍然是 log(n)。 我認為以這種方式使用跳過列表足夠新穎,值得自己寫一篇文章。

所以在這里。 我希望你覺得它很有趣。

我發現並發優先級隊列的一個很好的例子在這里 希望能對你有所幫助。

var priorityQueue = new ConcurrentPriorityQueue<TKey, TValue>();

此隊列上下文中的TKey可以是 int 值或任何其他實現 IComparable 的對象。

要使用這樣的隊列,您可以執行以下操作:

var priorityQueue = new ConcurrentPriorityQueue<int, object>(); 

// Add elements
priorityQueue.Enqueue(2, elementP2); 
priorityQueue.Enqueue(1, elementP1);

// Here you will receive elementP1
bool result = priorityQueue.TryDequeue(out KeyValuePair<int, object> element);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM