如何从另一个线程正确地更新ObservableCollection
本文关键字:正确地 更新 ObservableCollection 线程 另一个 | 更新日期: 2023-09-27 17:51:19
如果我使用ActionBlock
进行数据库调用,并且需要更新GUI(可能是ObservableCollection
)。是循环通过结果集和使用Dispatcher.BeingInvoke
一个很好的解决方案,还是有一个更好的方法?
我想一次加载一行到GUI,因为即使启用了虚拟化,似乎如果我一次更新整个可观察集合,GUI将挂起,直到它可以渲染整个数据网格。
一些模拟情况的示例代码:
ActionBlock<Func<Task>> _block = new ActionBlock<Func<Task>>(action => action());
_block.Post(async () =>
{
await Task.Delay(1000); // Perhaps Long database read
for (int i = 0; i < 1000000; i++) // Perhaps looping over database result set
{
await Dispatcher.BeginInvoke( // Need to update GUI
new Action(
() =>
{
// Add new object to collection (GUI will update DataGrid one row at a time).
MyModel.MyCollection.Add(new MyClass() { MyInt = i });
}
), DispatcherPriority.Background
);
}
});
如果您添加调用调度。BeginInvoke内部循环,然后你更新你的UI 100k次。理想情况下,我会这样做:
//do as much work as possible in background thread
var items = new MyClass[100000];
for (int i = 0; i < 1000000; i++) {
items[i] = new MyClass{ MyInt = i;}
}
Dispatcher.BeginInvoke(new Action(() => //update UI just once
MyModel.MyCollection = new ObservableCollection(items);
));
如果你的虚拟化工作,应该没有问题。
为了避免在UI线程中添加大量数据,您可以将其拆分为更小的数据部分:
for (int i = 0; i < 100; i++){
await Dispatcher.BeginInvokenew Action(() =>
{
for (int j = 0; j < 1000; j++) { //add thousand items at once
MyModel.MyCollection.Add(items[i * 1000 + j])
});
}
正在循环遍历结果集并使用Dispatcher。调用一个好的解决方案,还是有更好的方法?
在现代应用程序中使用Dispatcher.BeginInvoke
从来没有一个好的理由。
在你的情况下,因为你已经使用TPL数据流,你可以只是改变你的ActionBlock
到TransformManyBlock
,并将其链接到一个单独的ActionBlock
,在UI线程上执行。比如:
var _getRowsBlock = new TransformManyBlock<Func<Task<IEnumerable<TRow>>>, TRow>(
action => action());
var _updateUiBlock = new ActionBlock<TRow>(row =>
{
MyModel.MyCollection.Add(new MyClass() { MyInt = i });
}, new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
});
_getRowsBlock.LinkTo(_updateUiBlock, new DataflowLinkOptions { PropagateCompletion = true });
_block.Post(async () =>
{
await Task.Delay(1000); // Perhaps Long database read
return result.Rows; // return the database result set
});
我想一次加载一行到GUI,因为即使启用了虚拟化,似乎如果我一次更新整个可观察集合,GUI将挂起,直到它可以渲染整个数据网格。
好吧,那么你可能在寻找错误的解决方案。我看不出一次添加一行数据会有什么帮助。如果用户界面一次添加1000000行,那么一次添加1000000行会使它变得更加沉重……
你可能不得不考虑一个解决方案,你不加载1000000行到你的UI