C#如何在任务中更新用户界面

4

我刚接触 C#Task 和线程。

我有以下代码:

public void UpdateSales(object sender, EventArgs args)
{
    Task.Run(() =>
    {
    // Some code Create Collection ...
    // Some code with business logic  ..


    // Below code is to update UI
    // is it safe to update UI like below 

       saleDataGrid.Dispatcher.Invoke((Action) (() =>
                                {

                                    saleDataGrid.ItemsSource = currentCollection;
                                    saleDataGrid.Items.Refresh();
                                })); 

    });
}

我不确定这段代码是否正确。我认为无论如何都可能发生死锁现象?

请问您如何从Task中更新UI呢?由于UpdateSales是第三方库的事件处理程序,因此我没有使用async/await


3
通常你只需调用 Dispatcher 并检查是否需要在它特定的线程上调用,但我相信你的代码不会引起死锁。 - Callum Linington
2个回答

12

你知道,Task.Run会在线程池线程上执行。

然后你可以使用ContinueWith,它将在该任务完成时运行 - 如果你选择允许你指定TaskScheduler的重载之一,则你可以使用TaskScheduler.FromCurrentSynchronizationContext(),它将使用你进入方法时的同步上下文 - 如果这是 UI 线程(例如,你在 UI 事件的事件处理程序中),那么同步上下文将是 UI 线程的。

因此,你的代码将类似于以下内容:

Task.Run(() => {
    //...code to create collection etc...
}).ContinueWith(() => {
    saleDataGrid.ItemsSource = currentCollection;
}).TaskScheduler.FromCurrentSynchronizationContext());

12

假设UpdateSales在UI线程上调用,更好的解决方案是:

public async void UpdateSales()
{
  var collection = await Task.Run(() =>
  {
    // Some code Create Collection ...
    // Some code with business logic  ..
    return currentCollection;
  });
  saleDataGrid.ItemsSource = collection;
  saleDataGrid.Items.Refresh();
}

正如我在博客中所描述的那样,await会自动在捕获的上下文(在这种情况下为UI上下文)上恢复。我更喜欢使用await的隐式上下文而不是直接使用Dispatcher:代码更短,更便携。


“UpdateSales”事件是从COM对象事件处理程序触发的。不确定是否为UI线程。我已经编辑了这个方法签名以使其正确。 - Varun Jain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接