这是个有趣的问题。听起来解决方案的关键是优先队列的阻塞变体。Java 有PriorityBlockingQueue
.NET BCL 的等价物,但不幸的是不存在。然而,一旦你有了一个,实施就很容易了。
class MyWorker
{
public string MyType {get; set;}
public static PriorityBlockingQueue<string, MyInfo> data;
public static void DoWork()
{
while(true)
{
MyInfo value;
if (data.TryTake(MyType, timeout, out value))
{
// OK, do work
}
}
}
}
实现 aPriorityBlockingQueue
并不是很困难。BlockingCollection
遵循与利用Add
和样式方法相同的模式,Take
我提出了以下代码。
public class PriorityBlockingQueue<TKey, TValue>
{
private SortedDictionary<TKey, TValue> m_Dictionary = new SortedDictionary<TKey,TValue>();
public void Add(TKey key, TValue value)
{
lock (m_Dictionary)
{
m_Dictionary.Add(key, value);
Monitor.Pulse(m_Dictionary);
}
}
public TValue Take(TKey key)
{
TValue value;
TryTake(key, TimeSpan.FromTicks(long.MaxValue), out value);
return value;
}
public bool TryTake(TKey key, TimeSpan timeout, out TValue value)
{
value = default(TValue);
DateTime initial = DateTime.UtcNow;
lock (m_Dictionary)
{
while (!m_Dictionary.TryGetValue(key, out value))
{
if (m_Dictionary.Count > 0) Monitor.Pulse(m_Dictionary); // Important!
TimeSpan span = timeout - (DateTime.UtcNow - initial);
if (!Monitor.Wait(m_Dictionary, span))
{
return false;
}
}
m_Dictionary.Remove(key);
return true;
}
}
}
这是一个快速的实现,它有几个问题。首先,我根本没有测试过它。其次,它使用红黑树(via SortedDictionary
)作为底层数据结构。这意味着该TryTake
方法将具有 O(log(n)) 复杂度。优先级队列通常具有 O(1) 的移除复杂度。优先级队列选择的典型数据结构是堆,但我发现跳过列表实际上在实践中更好,原因有几个。这些都不存在于 .NET BCL 中,这就是为什么我使用 aSortedDictionary
代替,尽管它在这种情况下性能较差。
我应该在这里指出,这实际上并不能解决毫无意义的Wait/Pulse
行为。它被简单地封装在PriorityBlockingQueue
类中。但是,至少这肯定会清理代码的核心部分。
它看起来不像您的代码每个键处理多个对象,但是在添加到字典时使用 aQueue<MyInfo>
而不是普通的旧的很容易添加。MyInfo