注意:这是我之前提到的here的后续问题。
简单地总结一下,我之前的问题是如何将BlockingCollection
数据绑定到WPF中的控件 - 这是通过使用CollectionViewSource解决的。
但是,我已经考虑了一下我的用例,并意识到简单地使用BlockingCollection
并不适合我。我想要以下行为;
例如;
同时提交8个工作项,最大并发级别为4.四个工作项应移入" Processing"国家,而其他四个仍然在"待定"。作为" Processing"中的每个项目。州完成,"待定"中的另一项国家被选中进行处理。项目处理完成后,将从工作项池中删除。这对用户实时可见。
我之前使用的方法遇到的问题是,当一个项目被拾取进行处理时,它会从视图中消失,因为它已被消耗掉了#34;致电GetConsumingEnumerable
。我真正想要的是将物品安全地从"待定"用于处理的池,但仍保留在视图中,以便在UI中显示状态更新(通过INotifyPropertyChanged
)。
我已经通过实际使用两个并发集合来解决项目从视图中消失的问题,然后将它们包装为单个CompositeCollection(我将其绑定到而不是使用ICollectionView
)
我已经实现了以下行为;
this.currentWorkItems = new ObservableConcurrentCollection<WorkItem>();
this.pendingWorkItems = new ObservableConcurrentCollection<WorkItem>();
this.compositeCollection = new CompositeCollection
{
new CollectionContainer { Collection = this.currentWorkItems},
new CollectionContainer { Collection = this.pendingWorkItems },
};
for (int i = 0; i < workConcurrencyFactor; i++)
{
Task.Factory.StartNew(this.ProcessWorkItems);
}
然后是我的Add
方法;
public void Add(WorkItem workItem)
{
this.pendingWorkItems.TryAdd(workItem);
}
最后,ProcessWorkItems
方法;
private void ProcessWorkItems()
{
while (true)
{
Thread.Sleep(100);
WorkItem workItem;
if (this.pendingWorkItems.TryTake(out workItem))
{
this.currentWorkItems.TryAdd(workItem);
workItem.Status = "Simulating First Step";
Thread.Sleep(1000);
workItem.Status = "Simulating Second Step";
Thread.Sleep(1000);
// Finished processing
this.currentWorkItems.TryTake(out workItem);
}
}
}
注意,我使用here中的ObservableConcurrentCollection
。
这样可以正常工作,但我觉得我在这里遗漏了一些东西,或者我可能会因为多个任务在没有其他任何事情真正发生的情况下经常睡觉和醒来而产生不必要的开销。此外,我觉得我有点滥用第二个ObservableConcurrentCollection
,基本上只是将它用作我正在处理的项目的保留区域,但我仍然希望可见。< / p>
有没有更好的方法解决这个问题?并发消费者处理集合&#34;到位&#34;的标准模式是什么,同时避免多个消费者抓住同一个项目?
答案 0 :(得分:1)
正如Patryk已经建议这是TPL Dataflow的一个很好的例子 - 我们在这里做了类似的事情(只是管道中的几个步骤,包括批处理和转换):
创建数据流块以处理任务和集合以保存所有任务:
var actionBlock = new ActionBlock<WorkItem>(item => ProcessWorkItem(item),
new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = threadCount });
var allItems = new ConcurrentDictionary<int, WorkItem>(); // or whatever fits
然后在Add
方法中:
public void Add(WorkItem workItem)
{
allItems.Add(workItem.Id, workItem);
actionBlock.Post(workItem);
}
在ProcessWorkItem
结束时执行allItems.Remove(workItem.Id)
。
P.S。:数据流块也非常快 - 我们在这里每秒进行几百次Post
次呼叫......