C# 通过大量数据的可观察收集批量更新UI组件WinRT
我有一个WinRT应用程序,每次从设备接收数据时都会发出通知。我还有一个UI控件,它与我希望添加新数据的可观察集合进行数据绑定 虽然我已经使它能够更新可观察的集合,但它会导致UI变得非常滞后,因为生成的数据量很快。因此,最好是批量更新,可能每几百毫秒更新一次 下面显示了代码片段。首先,我创建周期计时器C# 通过大量数据的可观察收集批量更新UI组件WinRT,c#,user-interface,event-handling,windows-runtime,dispatcher,C#,User Interface,Event Handling,Windows Runtime,Dispatcher,我有一个WinRT应用程序,每次从设备接收数据时都会发出通知。我还有一个UI控件,它与我希望添加新数据的可观察集合进行数据绑定 虽然我已经使它能够更新可观察的集合,但它会导致UI变得非常滞后,因为生成的数据量很快。因此,最好是批量更新,可能每几百毫秒更新一次 下面显示了代码片段。首先,我创建周期计时器 TimerElapsedHandler f = new TimerElapsedHandler(batchUpdate); CreatePeriodicTimer(f, new
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] });
}
List lst=new List();
异步无效myData_值已更改(GattCharacteristic发送方,GattValueChangedEventArgs args)
{
变量数据=新字节[args.CharacteristicValue.Length];
DataReader.FromBuffer(args.CharacteristicValue).ReadBytes(数据);
lst.Add(newfinancialstuff(){Time=“DateTime.UtcNow.ToString”(“mm:ss.ffffff”)”,Amount=data[0]});
}
然后我的批处理更新,称为perioidly
private void batchUpdate(ThreadPoolTimer source)
{
AddItem<FinancialStuff>(financialStuffList, lst);
}
private void批处理更新(ThreadPoolTimer源)
{
AddItem(财务列表,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();
});
}
}
}
public async void AddItem(observeCollection oc,列表项)
{
锁(项目)
{
if(Dispatcher.HasThreadAccess)
{
foreach(项目中的T项目)
oc.添加(项目);
}
其他的
{
Dispatcher.RunAsync(CoreDispatcherPriority.Low,()=>
{
oc.Clear();
对于(int i=0;i
虽然这似乎是可行的,但在几次更新之后,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] });
EDIT2:
由于AddItem方法已经在后台线程上,我认为您不需要运行Dispatcher.RunAsync。相反,我认为最好是阻止它,这样就不会导致对该部分代码的多次调用。尝试使用Dispatcher。改为运行。我已经更新了上面的代码示例以显示更改。你不应该再需要oc上的锁了,因为物品上的锁已经足够好了。此外,请验证Dispatcher.Run的语法是否正确。TTat的答案听起来似乎有道理。我只会建议使用即时模式渲染(如Direct2D)来进行高频数据可视化。你能提供更多关于这方面的信息吗?如果你可以使用C++ VisualStudio,它可以为DirectX、Direct2D和混合DirectX提供XAML的项目模板。描述如何在XAML应用程序中使用DirectX。如果你真的不想使用C++,你通常会使用在DirectX上面的一个管理包装器SARPDX。我加了锁,但没什么区别。用户界面很快就开始锁定,这肯定是图表导致了锁定。如果我删除绑定到observableCollection的图表,UI不会锁定idea。我认为您的意思是oc.Add(itemsToRender[I]),但无论哪种方式,对于大于20的值,它似乎都会很快锁定(它会锁定很长时间/不确定地锁定在30大小)。我不明白为什么这么慢!还有什么想法吗?老实说,我不太确定这里的语法是什么。如果我们想同步执行运行,那么我们可以在“await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,()=>”之前使用wait,但是在移除锁之后,它仍然非常慢。这就是您想要的吗?ThanksIt需要同步。对于同步和“BeginInvoke”,它应该是“Invoke”对于异步,但我认为您使用的是4.5版?只需键入Dispatcher.Run和intellisense就可以为您提供语法。
AddItem<FinancialStuff>(financialStuffList, lst);
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();
});
}
}
}
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]);
}
}
});
}
}