我正在使用TPL Dataflow构建处理管道。我将物品送入管道,然后每个物品独立地移动通过管道,因为它从一个块到另一个块反弹,并最终在最后发出。这个管道实际上更像是一个网络,因为每个项目可以采用稍微不同的路径。
但是,在某些时候,我想在UI中显示每个项目的图形预览,当项目在管道中移动时,它应该自动更新。例如,想象这些物品是星舰,我想渲染每个星舰的盾牌和剩余的光子鱼雷的状态(是的,我最近一直在重新观看DS9)。例如,我的屏幕可能如下所示:
Defiant: 72% shields, 10 Quantum
Enterprise: 98% shields, 25 Photon
当然,盾牌和鱼雷可以独立耗尽,并且可能由于多种原因而发生。如果我有一个处理UI呈现的块,它将一直发送垃圾邮件,例如:
Defiant: 80% shields, 12 Quantum
Enterprise: 100% shields, 30 Photon
Defiant: 79% shields, 12 Quantum
Defiant: 79% shields, 11 Quantum
Defiant: 79% shields, 10 Quantum
Enterprise: 98% shields, 30 Photon
Enterprise: 98% shields, 29 Photon
...
我想做的是制作一个按照船名汇总所有这些消息的块。这样,当UI块选择消费消息时(相对很少会发生),它不会收到每艘船的整个作战历史记录,但只会收到最近的状态。
那么,编写像这样的块的最佳方法是什么?我写了一个快速而肮脏的版本,但我对此并不满意:
public class KeyedBroadcastBlock<TKey, T>
: IPropagatorBlock<KeyValuePair<TKey, T>, KeyValuePair<TKey, T>>, IReceivableSourceBlock<KeyValuePair<TKey, T>>
{
private readonly IDictionary<TKey, T> itemsMap;
...
public KeyedBroadcastBlock()
{
itemsMap = new Dictionary<TKey, T>();
inputBlock = new ActionBlock<KeyValuePair<TKey,T>>(
kvp =>
{
lock (itemsMap) {
bool valueExists = itemsMap.ContainsKey(kvp.Key);
itemsMap[kvp.Key] = kvp.Value;
if (!valueExists) {
midBlock.Post(kvp.Key);
}
}
});
midBlock = new ActionBlock<TKey>(
key =>
{
lock (itemsMap) {
T value;
if (itemsMap.TryGetValue(key, out value)) {
itemsMap.Remove(key);
outputBlock.Post(new KeyValuePair<TKey, T>(key, value));
}
}
});
outputBlock = new BufferBlock<KeyValuePair<TKey, T>>();
inputBlock.Completion.ContinueWith(delegate
{
midBlock.Complete();
outputBlock.Complete();
});
}
public bool TryReceive(Predicate<KeyValuePair<TKey, T>> filter, out KeyValuePair<TKey, T> item)
{
return outputBlock.TryReceive(filter, out item);
}
public bool TryReceiveAll(out IList<KeyValuePair<TKey, T>> items)
{
return outputBlock.TryReceiveAll(out items);
}
...
}
(编辑:为了演示目的清理了代码)