【问题标题】:async await and shared objects异步等待和共享对象
【发布时间】:2013-03-15 20:22:21
【问题描述】:

我正在使用 async / await 模式对一个对象执行一些 CPU 繁重的操作(我的方法是可等待的),它按预期工作而不会阻塞 UI 线程。

但是,当我将对象作为参数传递给新窗口的 ctor 时(新窗口是需要访问已处理对象的日志记录窗口),我的 UI 线程会阻塞(我有点理解为什么)。

为了解决这个问题,我可以将计算的执行包装在一个

Task.Run(async () => { await _myObject.PerformCalculations(); });

然后简单地调用

var logWindow = new LogWindow(_myObject);
logWindow.Show();

这可行(当然),但是当对象引发事件时我有很多调度程序调用,如果没有 Task.Run 调用,我就不必处理这些。

所以,我的问题是,我能否以某种方式在不调用 Task.Run 的情况下仍然将对象传递到日志记录窗口而不阻塞我的 UI 线程?

编辑

我很抱歉这个精简的例子,我实际上是想让这个问题尽可能简单易懂,结果惨遭失败。

以更一般的方式:我有一个在特定条件下引发事件的对象。引发事件时,我想在 UI 上执行更新。事件总是从使用 Task.Run(...) 创建的线程中触发。因此,调度程序调用 UI 线程。所以,我想使用 async / await 执行计算(已经有效)并将对象传递到我的日志记录窗口(块)。

当我使用 Task.Run 时,当然一切正常,但无论我从 UI 线程订阅对象的事件,我都必须使用调度程序调用 UI 线程,因为事件从 Task.Run 线程触发。运行创建。我想避免这些调度程序调用。 事实上,如果我不将对象传递给日志记录窗口并调用 Show() 方法,UI 线程就不会阻塞,一切都会按预期工作。事件处理程序中不需要调度程序调用。

【问题讨论】:

  • 当您已经在调用 Task.Run 时,无需使用异步委托。只需使用Task.Run(_myObject.PerformCalculations)
  • 哇。乔恩斯基特本人。 :) 谢谢!但是,它并不像我的示例所暗示的那么简单。我必须使用委托,因为我有多个命令,实际上我将命令包装在委托内的 try/catch 块中。
  • logWindow.Show();粘贴代码时不知何故丢失了。
  • 但即便如此,您真的需要它作为异步委托吗?老实说,你的问题真的不清楚。
  • 你的问题我不清楚。当对象引发事件时,您提到调度程序调用。能详细点吗?

标签: c# wpf multithreading asynchronous async-await


【解决方案1】:

很难清楚地了解您的情况。但在我看来,你可以通过一些重构来完成。

考虑PerformCalculations:这是一个返回Task的方法(因此将自己宣传为async-friendly),它会占用大量CPU(因此实际上不是存在 async-friendly) .我首先要看的是划分PerformCalculations 中的逻辑,以便CPU 绑定部分使用它们自己的Task.Run,留下PerformCalcuations 作为async 方法,它不会(直接)影响CPU :

public async Task PerformCalculationsAsync()
{
  while (...)
  {
    await Task.Run(<next calculations>);
    RaiseEvent();
  }
}

此重构的重点是将Task.Run 中的 CPU 代码与 UI-ish 代码引发事件分开。如果您的事件在逻辑上是进度更新,或者这种重构对您的代码来说太难了,还可以考虑使用标准的IProgress&lt;T&gt; 方法; IProgress&lt;T&gt;.Report 可以从任何线程调用。

您还可以在 async constructorsasync properties(尤其是数据绑定部分)上找到我的帖子对您​​有所帮助。

【讨论】:

    【解决方案2】:
    // awaitable as well - await this in another async void/Task method (e.g. commanding)
    public async Task MyAsyncProcess()
    {
        await _myObject.PerformCalculations();
        var logWindow = new LogWindow(_myObject);
    }
    

    Task.Run() 同步执行异步方法,因此在这种情况下,您几乎没有使用 async/await 模式。制作执行代码的方法async 并在其他地方等待它。或者使用一些FireAndForget 逻辑。

    【讨论】:

    • 我不确定这与我的问题有何关系?阻塞的不是 PerformCalculations() 调用,而是创建一个新窗口并传入对该对象的引用。
    • 嗯。现在当我调用 logWindow.Show(); 时 LogWindow 根本不显示
    • 这对我有用Caliburn.Micro,你的代码中的命令是如何实现的?
    • 我根本不对对象执行任何用户输入。如果这就是你所说的指挥的意思。
    • 好了,别管指挥了。您是否在像 App ctor 这样的同步上下文中调用此方法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-02
    • 2023-03-04
    • 2020-12-17
    • 1970-01-01
    • 2017-04-17
    相关资源
    最近更新 更多