これは興味深い質問です。ソリューションの鍵は、プライオリティ キューのブロッキング バリアントにあるようです。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
}
}
}
}
a の実装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
第 2 に、基礎となるデータ構造として( 経由で) 赤黒ツリーを使用します。つまり、TryTake
メソッドの複雑さは O(log(n)) になります。通常、プライオリティ キューの削除の複雑さは O(1) です。プライオリティ キューに選択される一般的なデータ構造はheapですが、いくつかの理由から実際にはスキップ リストの方が優れていることがわかりました。これらはどちらも .NET BCL には存在しないためSortedDictionary
、このシナリオではパフォーマンスが劣るにもかかわらず、代わりに を使用しました。
ここで、これは実際には無意味な動作を解決しないことを指摘しておきWait/Pulse
ます。PriorityBlockingQueue
クラスにカプセル化されているだけです。ただし、少なくともこれにより、コードのコア部分が確実にクリーンアップされます。
コードがキーごとに複数のオブジェクトを処理しているようには見えませんでしたが、ディクショナリに追加するときにQueue<MyInfo>
、単純な古いオブジェクトの代わりに aを使用することで簡単に追加できます。MyInfo