具有Async / Await的通用Windows UI响应性

问题描述 投票:0回答:1

[请帮助我了解如何正确等待长时间执行的任务以使UI在Universal Windows应用程序中保持响应。

在下面的代码中,OperateSystem是一个继承了ObservableObject的模型类。 OperateSystem.GetLatestDataFromAllDevices连接到各种仪器,收集数据,并使用来自仪器的信息更新类属性。视图将根据Operate系统中的值进行更新。

UI在运行dispatcher.RunAsync任务时没有响应,我向Thread.Sleep(5000)添加了GetLatestDataFromAllDevices()以确保它锁定UI 5秒钟。如果没有等待Task.Delay(refreshTimer),则UI永远不会更新(我假设它会在UI可以更新之前立即返回到GetLatestDataFromAllDevies中)。将refreshTimer设置为1ms可以更新UI,但是我知道这是另一个需要解决的问题的解决方法。

   public ProductionViewModel()
    {
        OperateSystem = new OperateSystem();
        StartButtonCommand = new RelayCommand(StartMeasurementSystem);
        StopButtonCommand = new RelayCommand(StopMeasurementSystem);

        if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
        {
            dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
        }
    }

    private async void StartMeasurementSystem()
    {
            stopRequest = false;
            StopButtonEnabled = true;
            StartButtonEnabled = false;
            while (!stopRequest)
            {
                await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => OperateSystem.GetLatestDataFromAllDevices(ConfigurationSettingsInstance));
                await Task.Delay(refreshTimer);
            }
    }

在OperateSystem中

    internal void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
    {
        GetDataInstrument1(configurationSettings);
        GetDataInstrument2(configurationSettings);
        GetDataInstrument3(configurationSettings);
        GetDatainstrumetn4(configurationSettings);
    }

每个GetDataInstrumnet方法都连接到仪器,收集数据,执行一些缩放/格式化,并使用当前值更新类属性。

我关注了其他与使用其他异步方法一样使用dispatcher.RunAsync的答案,我会得到线程混搭错误。但是现在我认为调度程序无论如何都只是在UI线程上编组这些任务,因此它仍会阻止UI udpates。

为了重新创建线程编组错误,我使GetLatestDataFromAllDevices异步,并等待作为任务执行的方法。

    internal async void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
    {
        await Task.Run(()=>GetDataInstrument1(configurationSettings));
        GetDataInstrument2(configurationSettings);
        GetDataInstrument3(configurationSettings);
        GetDatainstrumetn4(configurationSettings);
    }

这导致:System.Exception:'该应用程序调用了为另一个线程编组的接口。 (来自HRESULT的异常:0x8001010E(RPC_E_WRONG_THREAD))'

我已经在圈子中重构了几次,并且一直遇到线程封送错误或用户界面不响应的情况,完成此工作的好方法是什么?

c# multithreading uwp async-await mvvm-light
1个回答
2
投票

我已经在圈子中重构了几次,并且一直遇到线程封送错误或用户界面不响应的情况,完成此工作的好方法是什么?

由于用户界面不响应,因此必须将工作推送到后台线程(例如Task.Run)。

为了将更新编组回UI线程,我建议(按照优先顺序):

  1. 使用异步方法的返回值。例如MyUiProperty = await Task.Run(() => MyBackgroundMethod(...));
  2. 使用Progress<T>从异步方法获取多个值。例如var progress = new Progress<string>(update => MyUiProperty = update); await Task.Run(() => MyBackgroundMethod(..., progress));
  3. 捕获背景类中的SynchronizationContext,并将其用于将更新发送到UI线程。不建议这样做,因为这会导致后台驱动UI,而不是反过来。
© www.soinside.com 2019 - 2024. All rights reserved.