這是一個有趣的問題。這聽起來像解決方案的關鍵是priority queue的阻止變體。 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
}
}
}
}
實施PriorityBlockingQueue
並不是非常困難。跟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;
}
}
}
這是一個快速實施,它有幾個問題。首先,我還沒有測試過它。其次,它使用紅黑樹(通過SortedDictionary
)作爲基礎數據結構。這意味着TryTake
方法將具有O(log(n))複雜性。優先級隊列通常具有O(1)去除複雜性。典型的優先級隊列數據結構是heap,但我發現skip lists在實踐中實際上更好,原因有幾個。 .NET BCL中都沒有這些,這就是爲什麼我使用了SortedDictionary
,儘管在這種情況下性能較差。
我在這裏應該指出,這實際上並沒有解決無意義的Wait/Pulse
行爲。它只是封裝在PriorityBlockingQueue
類中。但是,至少這肯定會清理你的代碼的核心部分。
它似乎沒有像您的代碼每個鍵處理多個對象,但添加到字典時使用Queue<MyInfo>
而不是簡單的舊MyInfo
可以很容易地添加。
只是一個想法,每個對象類型都有它自己的鎖對象,所以你可以'Monitor.Pulse'適當的對象? – 2010-12-16 16:00:00
@deltreme:是的,這是可能的,但我不想爲每種類型創建一個新的對象。 – Xodarap 2010-12-16 16:02:11
我不確定你的意思是太多的類型爲每個隊列單獨排隊。怎麼會這樣?這是實現它的自然方式,我不確定有多少單獨的隊列比其他任何事件處理機制效率低。 – 2010-12-16 16:10:33