我有一个生产者/消费者队列,除了有特定类型的对象。因此,不只是任何消费者都可以使用添加的对象。我不想为每种类型创建一个特定的队列,因为有太多。 (它有点延伸了生产者/消费者的定义,但我不确定正确的术语是什么。)
EventWaitHandle是否允许带参数的脉冲?例如myHandle.Set(AddedType = "foo")
。现在我正在使用Monitor.Wait
,然后每个消费者都会检查脉冲是否真的是针对他们的,但这似乎毫无意义。
我现在拥有的pseduocode版本:
class MyWorker {
public string MyType {get; set;}
public static Dictionary<string, MyInfo> data;
public static void DoWork(){
while(true){
if(Monitor.Wait(data, timeout)){
if (data.ContainsKey(MyType)){
// OK, do work
}
}
}
}
}
正如你所看到的,当其他东西被添加到字典中时,我可能会得到脉冲。我只关心将MyType添加到dict中。有没有办法做到这一点?这不是什么大不了的事,但是,例如,我现在必须手动处理超时,因为每次锁定都可以在超时内成功,但MyType
永远不会添加到timeout
内的dict中。
答案 0 :(得分:3)
这是一个有趣的问题。听起来这个解决方案的关键是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
可以轻松添加。
答案 1 :(得分:1)
您似乎希望将生产者/消费者队列与Observer模式相结合 - 通用消费者线程或线程从队列中读取,然后将事件传递给所需的代码。在这种情况下,您实际上不会发信号通知观察者,而只是在消费者线程识别出对给定工作项感兴趣的人时调用它。
.Net中的观察者模式通常使用C#事件实现。您只需要调用该对象的事件处理程序,并通过它调用一个或多个观察者。目标代码首先必须通过将自己添加到事件中来自己注册观察对象,以便在工作到达时通知。