繁体   English   中英

如何从另一个线程正确更新ObservableCollection?

[英]How to properly update the ObservableCollection from another thread?

如果我使用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
                    );
                }
            });

如果在循环内添加调用Dispather.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.BeingInvoke是一个好的解决方案,还是有更好的方法?

在现代应用程序中从来没有充分的理由使用Dispatcher.BeginInvoke

在您的情况下,由于您已经在使用TPL Dataflow,您可以将ActionBlock更改为TransformManyBlock ,并将其链接到在UI线程上执行的单独的ActionBlock 就像是:

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也将挂起,直到可以呈现整个数据网格为止。

好吧,那么您可能正在寻找错误的解决方案。 我不知道一次添加一行数据会有什么帮助。 如果UI一次添加了1000000行,那么一次添加1000000行将使它更多……

您可能需要考虑向UI加载1000000行的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM