看起来.NET 4.0 在并发方面有很多改进,可能需要使用并发优先队列。在框架内是否有可重用的体面优先队列实现?
看起来.NET 4.0 在并发方面有很多改进,可能需要使用并发优先队列。在框架内是否有可重用的体面优先队列实现?
在msdn上有一个作为“使用.NET Framework进行并行编程的示例”一部分的实现。请参见ParallelExtensionsExtras。
文件ConcurrentPriorityQueue.cs的源代码直接链接
基本上,您将插入适当优先级的队列中。然后,在消费者端,您将按优先级从高到低的顺序遍历列表,检查队列是否非空,如果是,则消耗一个条目。
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年,但为了后人,我想用我的实现来回答。
请查看.NET Framework 4中的线程安全集合及其性能特征,但据我所知,没有现成的优先队列可供使用。所有新的线程安全集合都不维护顺序,但您可以在它们之上构建自己的优先队列。请参考@Steven的方法。
由于所有当前的答案都已过时或没有可行的解决方案,因此有一个 MSDN上的实现是可用的。请注意,此实现中较低优先级的内容会被首先处理。
http://www.research.ibm.com/people/m/michael/ipl-1996.pdf
这个算法允许多个线程同时使用堆结构,而不会因为一次锁定整个堆而导致损坏或死锁,因为它支持对树的部分进行细粒度锁定。您需要进行基准测试,以确定额外锁定和解锁操作的开销是否比锁定整个堆产生的争用更大。
3)如果您想完全避免锁定,另一个算法(在上面的链接中提到)建议使用请求的FIFO队列(可以轻松实现且无需锁定),以及一个单独的线程,该线程是唯一接触堆的东西。您需要测量以查看使用同步对象在线程之间切换焦点的开销与纯直接锁定的开销相比,哪种方式的开销更大。
在开始之前,值得一试的是,先看看使用锁定的简单实现会有多糟糕。它可能不是最有效的实现,但如果它仍然比您所需的执行速度快几个数量级,那么维护的便利性(即任何人,包括您自己一年后,都能够简单地查看代码并理解其作用)可能会超过在排队机制中花费的微小CPU时间。
希望这可以帮助您 :-)
https://www.codeproject.com/Articles/13295/A-Priority-Queue-in-C
我发现已经有一个优先队列[^]类被写出来了。然而,我想到我可以很容易地使用我的老朋友跳表来编写自己的优先队列。这样做的好处是出队操作只需要O(1)的时间,而平均入队操作仍然是log(n)。我认为以这种方式使用跳表是足够新颖的,值得一篇独立的文章。
所以这就是它。我希望你觉得它有趣。
我找到了一个很好的并发优先队列的例子这里。希望它能对你有所帮助。
var priorityQueue = new ConcurrentPriorityQueue<TKey, TValue>();
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);