我跟着this tutorial,创建了一个优先级队列并用阻塞集合包装它。我有一个DataGrid,我已连接到发出更改事件的底层优先级队列。我可以从UI线程中将项目添加到集合中,并且当缓冲区已满时它会阻塞。
现在我如何消费这些物品?这就是我所拥有的:
public DownloadViewModel()
{
Queue = new ConcurrentPriorityQueue<DownloadItem>(10);
Buffer = new BlockingCollection<KeyValuePair<int, DownloadItem>>(Queue, 10000);
Task.Factory.StartNew(() =>
{
KeyValuePair<int, DownloadItem> item;
while(!Buffer.IsCompleted)
{
if(Buffer.TryTake(out item))
{
// do something with the item
}
Thread.SpinWait(100000);
}
});
}
但是当我添加Task.Factory.StartNew
位时,我的应用程序突然在窗口出现之前需要30秒(在它出现之前),当我添加项目时我得到异常
这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。
我理解,但是真的有必要使用UI线程来获取项目吗?这不是打败了使用这个BlockingCollection的全部目的吗?我想创建4到8个消费者并使它们并行运行。
这应该怎么做?
答案 0 :(得分:2)
使用调度程序包装CollectionChanged
事件似乎工作得很好......
public bool TryAdd(KeyValuePair<int, T> item)
{
int pos = _queues.Take(item.Key + 1).Sum(q => q.Count);
_queues[item.Key].Enqueue(item.Value);
Interlocked.Increment(ref _count);
Dispatcher.BeginInvoke(
new Action(
() =>
NotifyCollectionChanged(
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, pos))
));
return true;
}
只需从ConcurrentPriorityQueue
派生DispatcherObject
。我认为这是应该如何完成的。
更简单,只需编写NotifyCollectionChanged
方法,如下所示:
private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
{
lock (CollectionChanged)
{
if (CollectionChanged != null)
Dispatcher.BeginInvoke(new Action(() => CollectionChanged(this, e)));
}
}
然后您不必使用BeginInvoke
将其他方法丢弃。
答案 1 :(得分:1)
[评论问题后,然后]
您不需要“使用UI线程获取项目”。但是,由于处理消费任务中的项目而对UI的任何更新都需要被分派到UI线程。分开你的担忧!