如何在MvvmCross/WPF应用中处理临时文件清理及异步等待
解决MvvmCross桌面应用关闭时异步任务未完成导致临时文件残留的问题
核心思路
通过在ViewModel中跟踪所有运行中的异步任务,在窗口关闭时拦截关闭请求,等待所有异步任务执行完毕(完成临时文件清理)后再允许应用关闭。以下分WPF和Cocoa平台给出具体实现方案:
1. ViewModel层:维护异步任务生命周期
在ViewModel中添加任务集合,跟踪所有正在执行的异步命令,同时提供等待所有任务完成的方法:
public class TipViewModel : MvxViewModel { // 线程安全集合,存储正在运行的异步任务 private readonly ConcurrentBag<Task> _runningTasks = new ConcurrentBag<Task>(); public IAsyncCommand ProcessTempFileCommand => new MvxAsyncCommand(ProcessTempFileAsync); private async Task ProcessTempFileAsync() { var workTask = ExecuteFileOperationsAsync(); _runningTasks.Add(workTask); try { await workTask.ConfigureAwait(false); } finally { // 任务完成后从集合移除 _runningTasks.TryTake(out _); } } // 实际业务逻辑:创建临时文件+异步操作+清理 private async Task ExecuteFileOperationsAsync() { var tempFilePath = Path.GetTempFileName(); try { // 模拟耗时异步操作 await Task.Delay(3000).ConfigureAwait(false); // 此处添加实际业务代码 } finally { // 确保临时文件被清理 if (File.Exists(tempFilePath)) File.Delete(tempFilePath); } } // 对外提供的等待所有任务完成的方法 public async Task WaitForAllTasksAsync() { if (_runningTasks.Any()) { await Task.WhenAll(_runningTasks).ConfigureAwait(false); } } }
2. WPF平台:拦截窗口关闭事件
在继承自MvxWpfView的窗口中,重写OnClosing方法,阻止立即关闭,等待ViewModel任务完成后再关闭窗口:
public partial class TipView : MvxWpfView { protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); var viewModel = DataContext as TipViewModel; if (viewModel == null) return; // 阻止窗口立即关闭 e.Cancel = true; // 异步等待任务完成后再关闭 _ = FinishCloseAfterTasks(viewModel); } private async Task FinishCloseAfterTasks(TipViewModel viewModel) { try { await viewModel.WaitForAllTasksAsync().ConfigureAwait(true); } catch (Exception ex) { // 此处添加异常日志记录逻辑 } finally { // 回到UI线程执行窗口关闭 Dispatcher.Invoke(() => Close()); } } }
3. Cocoa平台:拦截窗口关闭回调
在继承自MvxWindowController的窗口控制器中,实现WindowShouldClose方法,同步等待ViewModel任务完成:
public class TipWindowController : MvxWindowController { public override bool WindowShouldClose(NSObject sender) { var viewModel = ViewModel as TipViewModel; if (viewModel == null) return true; // 同步等待所有异步任务完成(Cocoa关闭回调为同步逻辑) viewModel.WaitForAllTasksAsync().GetAwaiter().GetResult(); return true; } }
关键注意事项
- 死锁避免:ViewModel中的异步方法使用
ConfigureAwait(false),避免捕获UI同步上下文导致死锁。 - 异常处理:等待任务过程中必须捕获异常,防止未处理异常导致应用崩溃。
- 任务准确性:确保每个异步任务都在
finally块中从集合移除,即使任务抛出异常也能维持集合的正确性。
内容的提问来源于stack exchange,提问作者Serg
相关产品推荐
相关产品推荐

