3

在我正在处理的应用程序中,每秒接收到数千个更新。立即将这些更新一一反映在 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);
            }
        }
    }
}
4

3 回答 3

3

我会考虑使用 Reactive Extensions ReactiveExtensions。此链接解决了类似的问题股票交易示例

于 2012-10-01T05:41:02.883 回答
1

我建议对您的代码进行两项更改。

首先,与其让线程休眠,不如创建一个每 50 毫秒触发一次的计时器。然后,该计时器处理程序将TryTake毫无延迟地循环,收集当前在集合中的所有项目(或最多某个最大值)。没有延迟,TryTake如果集合为空,将立即返回。

其次,不要为每次更新调用一次“更新 UI”。修改更新处理程序,使其接受整个更新列表,而不是一次接受一个更新。这将避免等待 UI 线程所涉及的时间。

当然,以上假设您已经修改了更新处理程序,以便它可以ObservableCollection一次性添加多个项目,从而防止多个CollectionChanged事件。

于 2012-09-30T22:25:32.770 回答
0

我第二个 Rx。具体来说,您需要 BufferByTime(如果在传入速率高时进行聚合)或 Throttle(如果在传入速率太高时丢弃值)。此外,您的 UI 可能应该绑定到 BehaviorSubject,它将始终缓存最后一个值,以便新订阅者在订阅时立即获得最后一个缓存的值。

于 2013-01-11T19:54:52.817 回答