应用程序暂停时访问UI线程?

Mar*_*ark 6 c# multithreading suspend uwp windows-10-universal

我有一个UWP应用程序,允许用户创建和修改文本文档.我很难让保存机制与app suspend/resume生命周期一起工作.

这是我有的:

  1. 所有磁盘访问都在单独的后台线程上完成,以保持UI响应
  2. 该后台线程还序列化所有磁盘访问,以确保始终只进行一次读取或写入操作
  3. 保存文档之前和之后,UI将更新以指示正在进行保存.这是在UI线程上安排的Dispatcher.RunAsync()

当应用程序被暂停时:

  • 暂停时,我必须最后一次保存文档以确保所有更改都在磁盘上(应用程序可能会在暂停时终止)
  • 所以我要求一个 ExtendedExecutionSession
  • 我在队列上安排了一个最终的保存操作
  • 然后我等待队列处理所有挂起的磁盘访问操作
  • 最后,我将扩展执行会话标记为已完成

我的问题:

  • 已安排的保存操作完成其磁盘访问,然后尝试通过主线程更新UI Dispatcher.RunAsync().后台队列等待此任务完成,但它永远不会,因为到那时,UI线程已经停止.

→所以我的最终保存操作永远不会执行,因为后台队列等待更新UI.

这是一个流程图:

应用程序暂停流程图

出现的一些问题:

  1. 当应用程序被暂停时,UI线程到底是什么时候停止的?只有当它闲置或"介于两者之间"时?即如果我安排了一个块通过Dispatcher.RunAsync(),这至少会完成或"冻结"吗?
  2. 有没有办法让我检查当前窗口的UI线程是否已经停止,以便我知道我不能再在后台文件访问线程中访问它了?
  3. 当应用程序被暂停时,哪些线程会停止以及何时停止?
  4. 应用程序暂停时,我的后台文件访问线程是否也有被阻止的风险?

总结一下这个问题:

当应用程序被暂停时,我必须确保在等待后台线程上的潜在挂起磁盘访问权限才能完成,然后我再次保存文档,以防我的应用程序稍后终止.

Lai*_*ith 5

这听起来太熟悉了!

我从您的问题中了解到,保存操作以更新 UI 结束,但由于它在后台线程中运行,因此除非您使用Dispatcher. 只是为了形象化,您的代码是否看起来像这样:

public async Task SaveAsync()
{
   // .. save to disk ..

   await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
   {
      // update UI here
   }

   // Save Complete!
}
Run Code Online (Sandbox Code Playgroud)

如果是这样,问题是 UI 更新是排队后台操作的一部分,如果 UI 更新或它Dispatcher本身阻止了任何内容,则在您到达该行之前不会开始其他操作//Save Complete!,如果Dispatcher永远不会执行您的行动。

与其关注 UI 更新的保存操作,不如尝试公开将触发 UI 更新的事件。例如:

public event EventHandler SaveStarted;
public event EventHandler SaveCompleted;

public async Task SaveAsync()
{
   SaveStarted?.Invoke(this, EventArgs.Empty);

   // .. save to disk ..

   SaveCompleted?.Invoke(this, EventArgs.Empty);
}
Run Code Online (Sandbox Code Playgroud)

您的视图可以将处理程序附加到这些事件并显示/隐藏适当的视觉效果,而您的挂起处理程序可以安全地调用该SaveAsync()方法而无需担心 UI。

当视图为 时Unloaded,请确保分离这些事件处理程序。引发这些事件时,您的保存操作不会阻塞队列。


更新 1

是的,您的事件处理程序必须使用以下命令在 UI 线程上运行Dispatcher

public void OnSaveCompleted(object sender, EventArgs e)
{
   Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
   {
      // update UI here...
   }
}
Run Code Online (Sandbox Code Playgroud)

因为处理程序被强制返回void,所以不会阻塞后台线程。


更新 2

听起来你有一些紧耦合。您需要重新设计您的应用程序,以便您的文本文档逻辑位于一个单独的项目中。我总是在.Net Standard项目中编码,这会强制所有 UI。该项目将不允许任何 UI 代码,从而保持您的逻辑清晰和解耦。

如果您的所有代码都位于 UWP 项目中,那么很容易开始包括视图逻辑、XAML 控件,以及Dispatcher您的应用程序中与演示无关的部分。

在您的情况下,您需要弄清楚如何使您的文本文档逻辑成为文档状态的所有者,而不是 UI。hasChanges应该只由拥有SaveAsync(). 如果在 UI 操作期间出现问题/阻塞/失败/等,并且它永远不会成功地将其成功写入文本文档,那么这是一个不完整的操作,不应该是文档的一部分,也不应该是文档的责任担心关于它何时暂停并保存到磁盘。

如果 UWP 在这些操作之一期间出于任何原因终止了您的应用程序,那么它就消失了。您的下一个任务是弄清楚如何在应用程序再次唤醒时恢复此操作。