通过具有大量数据的可观察集合批量更新UI组件
本文关键字:集合 观察 更新 组件 UI 数据 | 更新日期: 2023-09-27 18:05:55
我有一个WinRT应用程序,每次从设备接收数据时都会触发通知。我还有一个UI控件,它绑定到一个可观察集合,我希望将新数据添加到
虽然我已经使它能够更新可观察集合,但它会导致UI变得非常滞后,因为数据量生成得很快。因此,最好是批量更新,也许每几百毫秒更新一次。
下面显示了代码片段。首先创建周期计时器
TimerElapsedHandler f = new TimerElapsedHandler(batchUpdate);
CreatePeriodicTimer(f, new TimeSpan(0, 0, 3));
下面是新数据传入时的事件处理程序,以及存储信息
的临时列表List<FinancialStuff> lst = new List<FinancialStuff>();
async void myData_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
var data = new byte[args.CharacteristicValue.Length];
DataReader.FromBuffer(args.CharacteristicValue).ReadBytes(data);
lst.Add(new FinancialStuff() { Time = "DateTime.UtcNow.ToString("mm:ss.ffffff")", Amount = data[0] });
}
然后是我的批处理更新,称为周期性
private void batchUpdate(ThreadPoolTimer source)
{
AddItem<FinancialStuff>(financialStuffList, lst);
}
最后,为了测试,我想清除可观察对象集合和项。
public async void AddItem<T>(ObservableCollection<T> oc, List<T> items)
{
lock (items)
{
if (Dispatcher.HasThreadAccess)
{
foreach (T item in items)
oc.Add(item);
}
else
{
Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
oc.Clear();
for (int i = 0; i < items.Count; i++)
{
items.Count());
oc.Add(items[i]);
}
lst.Clear();
});
}
}
}
虽然这似乎工作,几个更新后,UI锁定,它更新非常缓慢/如果没有。对于测试,到计时器触发时,它只在列表中获得几百个项目。
谁能告诉我为什么会发生这种情况?我认为我的设计很差。
谢谢
您没有在事件处理程序中锁定列表
// "lst" is never locked in your event handler
List<FinancialStuff> lst = new List<FinancialStuff>();
lst.Add(new FinancialStuff() { Time = "DateTime.UtcNow.ToString("mm:ss.ffffff")", Amount = data[0] });
将上面的"lst"传递给async方法
AddItem<FinancialStuff>(financialStuffList, lst);
你锁定了下面的"items",实际上是上面的"lst"。然而,当你处理它的时候,你正在向列表中添加内容。我假设事件处理程序具有更高的优先级,因此您的处理速度比添加慢。这可能导致"I <物品。永远都是真的。>
public async void AddItem<T>(ObservableCollection<T> oc, List<T> items)
{
// "lst" reference is locked here, but it wasn't locked in the event handler
lock (items)
{
if (Dispatcher.HasThreadAccess)
{
foreach (T item in items)
oc.Add(item);
}
else
{
Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
oc.Clear();
// This may never exit the for loop
for (int i = 0; i < items.Count; i++)
{
items.Count());
oc.Add(items[i]);
}
lst.Clear();
});
}
}
}
编辑:你需要查看每一条数据吗?使用锁时将会有一些开销。如果你获取数据的速度比你渲染数据的速度快,你最终会被备份和/或有一个非常大的集合来渲染,这也可能会导致一些问题。我建议您做一些过滤,只绘制最后x个项目(比如100个)。而且,我也不确定你为什么需要if (Dispatcher.HasThreadAccess)
条件。
尝试以下操作:
public async void AddItem<T>(ObservableCollection<T> oc, List<T> items)
{
// "lst" reference is locked here, but it wasn't locked in the event handler
lock (items)
{
// Change this to what you want
const int maxSize = 100;
// Make sure it doesn't index out of bounds
int startIndex = Math.Max(0, items.Count - maxSize);
int length = items.Count - startIndex;
List<T> itemsToRender = items.GetRange(startIndex, length);
// You can clear it here in your background thread. The references to the objects
// are now in the itemsToRender list.
lst.Clear();
// Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
// Please verify this is the correct syntax
Dispatcher.Run(() =>
{
// At second look, this might need to be locked too
// EDIT: This probably will just add overhead now that it's not running async.
// You can probably remove this lock
lock(oc)
{
oc.Clear();
for (int i = 0; i < itemsToRender.Count; i++)
{
// I didn't notice it before, but why are you checking the count again?
// items.Count());
oc.Add(itemsToRender[i]);
}
}
});
}
}
EDIT2: 因为你的AddItem方法已经在后台线程,我不认为你需要运行Dispatcher.RunAsync。相反,我认为最好让它阻塞,这样您就不会多次调用该代码段。尝试使用Dispatcher。而不是运行。我已经更新了上面的代码示例以显示更改。你不应该再需要锁了,因为锁在物品上已经足够好了。另外,验证Dispatcher的语法。