これは興味深い質問です。ソリューションの鍵は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
の実装はそれほど難しくありません。 Add
とTake
スタイルのメソッドを使用してBlockingCollection
と同じパターンに従って、私は次のコードを思いついた。
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
クラスにカプセル化されています。しかし、これは少なくともあなたのコードの中核部分をきれいにするでしょう。
キーごとに複数のオブジェクトを処理するコードのようには表示されませんでしたが、辞書に追加するときには普通の古いMyInfo
の代わりにQueue<MyInfo>
を使用して簡単に追加できます。
それぞれのオブジェクトタイプには独自のロックオブジェクトがあるので、適切なオブジェクトを 'Monitor.Pulse'することができますか? –
@deltreme:それは可能ですが、私はそれぞれのタイプに対して新しいオブジェクトを作成したくありません。 – Xodarap
それぞれに別々のキューを作るには、あまりにも多くの型があるということが何を意味するのか分かりません。どうして?それを行うのは当然の方法です。キューを別々にすると他のイベント処理メカニズムより効率が悪くなります。 –