异步/等待,在调用后更新ui组件,"不同的线程拥有它"例外

时间:2016-08-18 22:12:19

标签: c# multithreading asynchronous async-await

我需要将一些网格列附加到网格中,该网格最初只包含几列。创建列是长期运行的,并且我尝试使用async / await,但是得到"调用线程无法访问此对象,因为另一个线程拥有它"例外情况,有人可以指导我这样做的正确方法。 AddRange调用发生异常。提前谢谢。

private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    List<GridViewColumn> cols = await NewGridColsAsync();
    viewGridControl.Columns.AddRange(cols);
}

private async Task<List<GridViewColumn>> NewGridColsAsync()
{
    List<GridViewColumn> cols = new List<GridViewColumn>();

    await Task.Run(() =>
    {
        for (int i = 0; i < 100; i++)
        {
            cols.Add(new GridViewColumn());
        }
    });

    return cols;
}

2 个答案:

答案 0 :(得分:5)

您正在一个线程(由GridViewColumn创建)上创建Task.Run个控件实例,并尝试将它们添加到其他线程(UI线程)上。由于在不同线程上创建的对象会出现异常。

正确实施应类似于以下代码。请注意,控件(GridViewColumn的实例)是使用此代码在UI线程上创建的,稍后可用于从主UI线程添加其他控件的子项:

private async Task<List<GridViewColumn>> NewGridColsAsync()
{
   IEnumerable<DataForColumn> dataForColumns;
   await Task.Run(() =>
   {
      // collect data for columns, runs on thread-pool (non-UI) thread
      dataForColumns = .... 
   });

    // runs on UI thread 
    return dataForColumns.Select(data => CreateGridViewColumn(data)).ToList();
}

答案 1 :(得分:2)

正如其他人所说,您必须在UI线程上创建UI对象。由于您没有CPU限制工作要做,因此不需要Task.Run

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
  List<GridViewColumn> cols = NewGridCols();
  viewGridControl.Columns.AddRange(cols);
}

private List<GridViewColumn> NewGridCols()
{
  List<GridViewColumn> cols = new List<GridViewColumn>();

  for (int i = 0; i < 100; i++)
  {
    cols.Add(new GridViewColumn());
  }

  return cols;
}

如果你有这么多的 UI元素,这个“无法”进入UI线程,那么你需要实现某种形式的虚拟化。 UI线程足够快,可以创建比人脑可以处理的UI元素更多的元素;如果你看到明显的延迟,那么人脑就无法处理那么多元素,而正确的答案就是使用虚拟化。