通用 Windows UI 响应 Async/Await
Universal Windows UI Responsiveness with Async/Await
请帮助我了解如何正确等待长时间执行的任务以保持 UI 在通用 Windows 应用程序中响应。
在下面的代码中OperateSystem是一个继承了ObservableObject的模型class。 OperateSystem.GetLatestDataFromAllDevices 连接到各种仪器,收集数据,并使用来自仪器的信息更新 class 属性。视图使用操作系统中的值进行更新。
UI 没有响应,而 dispatcher.RunAsync 任务是 运行,我向 GetLatestDataFromAllDevices() 添加了一个 Thread.Sleep(5000) 以确保它锁定将 UI 调高 5 秒。如果没有 await 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);
}
}
在操作系统中
internal void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
GetDataInstrument1(configurationSettings);
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
每个 GetDataInstrumnet 方法都连接到仪器、收集数据、执行一些 scaling/formatting,并使用当前值更新 class 属性。
我关注了其他 S.O。使用 dispatcher.RunAsync 的答案与使用其他异步方法一样,我会遇到线程混搭错误。但现在我认为调度程序只是在 UI 线程上编组这些任务,所以它仍然阻止 UI 更新。
为了重现线程编组错误,我使 GetLatestDataFromAllDevices 异步,并等待作为任务执行的方法。
internal async void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
await Task.Run(()=>GetDataInstrument1(configurationSettings));
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
这导致:
System.Exception: 'The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))'
我在圈子里重构了几次,并保持 运行 线程编组错误或无响应 UI,完成此任务的好方法是什么?
I've refactored in circles a few times and keep running into either thread marshaling errors or an unresponsive UI, what's a good way to get this done?
由于您有一个无响应的 UI,您必须将工作推送到后台线程(例如,Task.Run
)。
为了将更新编组回 UI 线程,我建议(按优先顺序):
- 使用异步方法的return值。例如,
MyUiProperty = await Task.Run(() => MyBackgroundMethod(...));
.
- 使用
Progress<T>
从异步方法中获取多个值。例如,var progress = new Progress<string>(update => MyUiProperty = update); await Task.Run(() => MyBackgroundMethod(..., progress));
.
- 在您的后台 类 捕获
SynchronizationContext
并使用它向 UI 线程发送更新。这是最不推荐的,因为它会导致你的背景驱动你的 UI 而不是相反。
请帮助我了解如何正确等待长时间执行的任务以保持 UI 在通用 Windows 应用程序中响应。
在下面的代码中OperateSystem是一个继承了ObservableObject的模型class。 OperateSystem.GetLatestDataFromAllDevices 连接到各种仪器,收集数据,并使用来自仪器的信息更新 class 属性。视图使用操作系统中的值进行更新。
UI 没有响应,而 dispatcher.RunAsync 任务是 运行,我向 GetLatestDataFromAllDevices() 添加了一个 Thread.Sleep(5000) 以确保它锁定将 UI 调高 5 秒。如果没有 await 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);
}
}
在操作系统中
internal void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
GetDataInstrument1(configurationSettings);
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
每个 GetDataInstrumnet 方法都连接到仪器、收集数据、执行一些 scaling/formatting,并使用当前值更新 class 属性。
我关注了其他 S.O。使用 dispatcher.RunAsync 的答案与使用其他异步方法一样,我会遇到线程混搭错误。但现在我认为调度程序只是在 UI 线程上编组这些任务,所以它仍然阻止 UI 更新。
为了重现线程编组错误,我使 GetLatestDataFromAllDevices 异步,并等待作为任务执行的方法。
internal async void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
await Task.Run(()=>GetDataInstrument1(configurationSettings));
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
这导致: System.Exception: 'The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))'
我在圈子里重构了几次,并保持 运行 线程编组错误或无响应 UI,完成此任务的好方法是什么?
I've refactored in circles a few times and keep running into either thread marshaling errors or an unresponsive UI, what's a good way to get this done?
由于您有一个无响应的 UI,您必须将工作推送到后台线程(例如,Task.Run
)。
为了将更新编组回 UI 线程,我建议(按优先顺序):
- 使用异步方法的return值。例如,
MyUiProperty = await Task.Run(() => MyBackgroundMethod(...));
. - 使用
Progress<T>
从异步方法中获取多个值。例如,var progress = new Progress<string>(update => MyUiProperty = update); await Task.Run(() => MyBackgroundMethod(..., progress));
. - 在您的后台 类 捕获
SynchronizationContext
并使用它向 UI 线程发送更新。这是最不推荐的,因为它会导致你的背景驱动你的 UI 而不是相反。