[请帮助我了解如何正确等待长时间执行的任务以使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))'
我已经在圈子中重构了几次,并且一直遇到线程封送错误或用户界面不响应的情况,完成此工作的好方法是什么?
我已经在圈子中重构了几次,并且一直遇到线程封送错误或用户界面不响应的情况,完成此工作的好方法是什么?
由于用户界面不响应,因此必须将工作推送到后台线程(例如Task.Run
)。
为了将更新编组回UI线程,我建议(按照优先顺序):
MyUiProperty = await Task.Run(() => MyBackgroundMethod(...));
。Progress<T>
从异步方法获取多个值。例如var progress = new Progress<string>(update => MyUiProperty = update); await Task.Run(() => MyBackgroundMethod(..., progress));
。SynchronizationContext
,并将其用于将更新发送到UI线程。不建议这样做,因为这会导致后台驱动UI,而不是反过来。