【问题标题】:DelegateCommand async await vs Task.Run UI lockingDelegateCommand async await vs Task.Run UI 锁定
【发布时间】:2020-07-16 09:36:49
【问题描述】:

我希望我的 UI 在单击按钮后显示加载叠加层。

选项 1

MyCommand = new DelegateCommand(() => Task.Run(MyLongRunningTask);

private async Task MyLongRunningTask() {
    IsLoading = true;
    Thread.Sleep(5000);
    IsLoading = false;
}

选项 2

MyCommand = new DelegateCommand(MyLongRunningMethod);

private async void MyLongRunningMethod() {
    IsLoading = true;
    Thread.Sleep(5000);
    IsLoading = false;
}

选项 1 有效,选项 2 无效。这种情况我很好,虽然我不明白为什么。 但我真的很想避免将每次执行都包装到 () => Task.Run(...) 中。

由于 async void 似乎是个坏主意,我想继续使用任务。但是我想缩短语法,所以我疯狂地尝试,但我不知道如何将 () => Task.Run(..) 放在派生自 DelegateCommand 的方法或类中。

为什么不编译?

    private DelegateCommand GetAsyncCommand(Task t)
    {
        return new DelegateCommand(() => Task.Run(t));
    }

任何与此类似的事情都会导致编译时错误或我不得不调用

MyCommand = GetAsyncCommand(MyLongRunningTask());

因为有 () 大括号,它会在实例化时立即执行我的任务。

这对我来说似乎真的很令人费解。如果我必须将所有长时间运行的任务包装到这个结构中,为什么我不能构建一个更智能的命令来为我完成一个任务?

编辑:我只是注意到选项 1 可能不会锁定 UI,但它会吞下可怕的异常。

如何设置不阻塞 UI 并正常抛出异常的命令?

【问题讨论】:

  • 让我引用 Stephen Cleary 的优秀文章 (docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/…):“如果调用者不希望它们是异步的,异步 void 方法可能会造成严重破坏。”
  • 他还写了一个AsyncCommand。这会给你一个干净的语法。 github.com/StephenCleary/Mvvm.Async/blob/master/src/…
  • Avoid async void。它仅适用于事件处理程序。
  • 方法MyLongRunningTask 应该给你一个警告,你不应该忽略:这个异步方法缺少'await'操作符,并且会同步运行。考虑使用“await”运算符来等待非阻塞 API 调用,或使用“await Task.Run(...)”在后台线程上执行 CPU 密集型工作。
  • “为什么不编译?” 因为Task.Run() 想要FuncAction,但你给它的是Task。编译器应该会告诉你类似的事情。

标签: c# wpf async-await command task


【解决方案1】:

Thread.Sleep() 正在锁定当前线程,无论它在哪里:同步或异步方法。 Async 方法有另一种暂停执行的方法 - await Task.Delay()

async 不对Threading 负责,async 实现了一个state mashine,该state mashine 执行您的代码,将其异步拆分为等待部分。 Task 负责线程。

替换

Thread.Sleep(5000);

await Task.Delay(5000);

请注意,async void 是不好的做法。 Look.

【讨论】:

  • 但这无助于将 UI 线程从 CPU 繁重的工作中解放出来。我的印象是Thread.Sleep(...) 是用于计算 CPU 繁重工作的模拟。如果是这种情况,明智的做法是使用Task.Run() 在线程池线程上运行该代码。
  • @Ackdari 同意,但您确定async Task 中的繁重工作正在当前线程中明确执行吗?我不是。而且我不确定如果我运行 2 个额外的重型异步方法,它会在第一个方法完成之前导致第二个方法中的线程锁定。因此Sleep() os 不是精确的“繁重任务”仿真,而是纯粹的锁定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-08
  • 2016-08-07
  • 2020-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多