.NET 4.0中的并发优先级队列

时间:2010-10-25 16:07:01

标签: c# .net concurrency

似乎.NET 4.0中有很多与并发相关的改进可能依赖于并发优先级队列。框架内是否有可用于重用的正常优先级队列实现?

9 个答案:

答案 0 :(得分:19)

在msdn上有一个实现作为“.NET Framework并行编程示例”的一部分。见ParallelExtensionsExtras

指向文件ConcurrentPriorityQueue.cs

的源代码的直接链接

答案 1 :(得分:6)

您可能需要自己动手。一种相对简单的方法是拥有一组常规队列,优先级降低。

基本上,您将插入队列以获得适当的优先级。然后,在消费者方面,您将从列表中下拉,从最高优先级到最低优先级,检查队列是否为非空,如果是,则使用条目。

答案 2 :(得分:5)

也许您可以使用我自己的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;
    }
}

答案 3 :(得分:2)

检查Thread-safe Collections in .NET Framework 4 and Their Performance Characteristics,但AFAIK没有准备好使用优先级队列。所有新的线程安全集合都不会维护顺序,但您可以在它们之上创建自己的集合。检查@ Steven的方式。

答案 4 :(得分:2)

由于目前所有答案都已过时或未提供可行的解决方案,因此可以使用implementation on MSDN。请注意,在此实现中首先处理较低优先级。

答案 5 :(得分:0)

选项:

1)如果您的队列不会变大,请使用堆并锁定整个结构以进行每次插入和删除。

2)如果你的队列变得很大,你可以使用这样的算法:

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

此算法允许多个线程同时使用堆结构,而不会立即支持对树的部分细粒度锁定,从而存在损坏或死锁的风险。您必须进行基准测试,以确定额外锁定和解锁操作的开销是否比锁定整个堆的争用成本高。

3)如果您的目标是完全避免锁定,上面链接中提到的另一种算法建议使用FIFO队列请求(可以轻松实现,没有锁定),以及一个单独的线程,它是唯一接触堆的东西。您必须测量以查看使用同步对象在线程之间切换焦点的开销与普通直接锁定的开销相比如何。

在你开始之前,看一下使用锁定直接实现的命中有多糟糕是值得的。它可能不是最有效的实现,但如果它仍然比你需要的速度快几个数量级,那么易于维护(也就是说,任何人,包括你自己一年都可以,只需查看代码并且理解它的作用)可能超过在排队机制中忙碌的CPU时间的一小部分。

希望这会有所帮助: - )

答案 6 :(得分:0)

最近,我正在创建一个状态机,我需要时间标记的事件。我需要使用自己的ID来定时事件,而不仅仅是一个简单的时钟滴答,这样我就能将一个事件与另一个事件区分开来。

研究这个问题让我想到了使用优先级队列。我可以按时间顺序排列定时事件及其信息;优先级队列将负责正确排序事件。计时器会定期检查优先级队列,以查看是否需要时间来触发队列头部的事件。如果是这样,它会对事件进行排队并调用与之关联的委托。这种方法正是我所寻找的。

在CodeProject搜索

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

我发现已经编写了优先级队列[^]类。然而,在我看来,我可以使用我的老朋友,跳过列表轻松编写自己的内容。这将具有以下优点:出队操作仅花费O(1)时间,而入队操作仍然是平均log(n)。我认为以这种方式使用跳过列表是足够新颖的,它值得自己的文章。

所以在这里。我希望你觉得它很有趣。

答案 7 :(得分:0)

我发现了并发优先级队列here的一个很好的例子。 希望它能对您有所帮助。

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

答案 8 :(得分:-1)

好吧,7年过去了,但是对于子孙后代,我想回答一下我的实施情况。

Documentation: Optionally awaitable simple to use Concurrent Priority Queue

Sourcecodes: github

nuget package

  • 无锁,
  • 高度并发,
  • 存储项类型中的通用,
  • 优先级类型的通用,但受限于.net枚举,强类型优先级,
  • 表示的优先级
  • 明确定义施工期间优先级的降序,
  • 检测项目数和每个优先级项目数的能力,
  • 出队的能力 - 优先顺序递减,
  • 覆盖出列优先级的能力,
  • 可能正在等待,
  • 基于潜在优先级的等待,