在我正在处理的应用程序中,每秒都会收到数千个更新。立即在UI上反映这些更新是性能上的过度杀伤。
以下代码会产生非常糟糕的性能,因为处理每个更新都需要调用UI线程并向ObservableCollection添加一个新项,然后它会触发CollectionChanged事件。
foreach (var update in updates.GetConsumingEnumerable())
{
// Handle Update
}
我要做的是让消费者等待一点时间(即50毫秒),让发布者有机会添加更多项目,然后以块的形式处理这些更新。
目前,我正在使用以下代码,但我发现很难预测这些项目在消费之前会保留多长时间,我担心这种方法可能会产生另一个性能瓶颈。
List<Tuple<T, List<string>>> updatesToHandle = new List<Tuple<T, List<string>>>();
while (!updates.IsAddingCompleted)
{
Tuple<T, List<string>> item = null;
if (updates.TryTake(out item, 5)) // Try Take for 5 Milliseconds
{
updatesToHandle.Add(item);
}
else
{
var newItems = new List<T>(updatesToHandle.Count);
var updatedItems = new List<Tuple<T, List<string>>>(updatesToHandle.Count);
foreach (var update in updatesToHandle)
{
try
{
// Handle Update
}
finally
{
updatesToHandle.Clear();
Thread.Sleep(50);
}
}
}
}
答案 0 :(得分:3)
我会考虑使用Reactive Extensions ReactiveExtensions。此链接解决了类似的问题Stock Trading Example
答案 1 :(得分:1)
我建议您对代码进行两处更改。
首先,不是让线程休眠,而是创建一个每50毫秒触发一次的计时器。然后,该计时器处理程序将在TryTake
上循环,没有延迟,收集当前集合中的所有项目(或最多一些)。如果集合为空,TryTake
将立即返回。
其次,每次更新都不要为“更新UI”调用一次。修改更新处理程序,使其接受整个更新列表,而不是一次更新一个更新。这样可以避免等待UI线程所需的时间。
当然,上面假设您修改了更新处理程序,以便它可以一次向ObservableCollection
添加多个项目,从而阻止多个CollectionChanged
事件。
答案 2 :(得分:0)
我是第二个Rx。具体而言,您需要BufferByTime(如果传入速率高时聚合)或Throttle(如果传入速率太高则丢弃值)。此外,您的UI应该绑定到BehaviorSubject,它将始终缓存最后一个值,以便新订阅者在订阅时立即获得最后一个缓存值。