c# - 在一种方法中调用同步和异步方法(api/UI)的正确方法是什么

标签 c# asynchronous

在下面的示例中,我在同步方法 (UI) 中调用异步方法。 在异步方法中,我调用了另一个异步方法(例如 api 调用),但我也调用了其他同步方法(例如更新组合框)。现在我对每个同步方法调用都使用 Invoke((MethodInvoker...

private void control_SelectionValueChanged(Object sender, EventArgs e)
{
  Task task = Task.Run(async () => await SomeMethodAsync());
}

private async Task SomeMethodAsync()
{
  Invoke((MethodInvoker)(() => SomeMethodA))
  bool variable = await SomeOtherMethodAsync()
  if ( variable ) Invoke((MethodInvoker)(() => SomeMethodB))
  Invoke((MethodInvoker)(() => SomeMethodC))
}

最佳答案

让我们分解一下这里发生的事情。

当您的 control_SelectionValueChanged处理程序触发,我假设我们正在 UI 线程上运行。然后你:

  • 开始 SomeMethodAsync通过 Task.Run 在线程池线程上.这不会阻塞 UI 线程。
  • 一旦线程池线程开始执行SomeMethodAsync您要求运行时通过调用 Control.Invoke 将您返回 到 UI 线程.同时 SomeMethodA正在 UI 线程上执行,同时您也在阻塞您的线程池线程。
  • 然后您解除线程池线程的阻塞并要求它执行其他一些async方法。整个操作将远离UI线程(除非 SomeOtherMethodAsync 里面有一些奇怪的东西,即另一个 Control.Invoke 调用)
  • await之后您返回到 a 线程池线程 - 这可能与 await 之前的线程池线程相同,或不同的 - 这取决于 TaskScheduler .
  • 如果variabletrue ,你执行 SomeMethodB在 UI 线程上(再次阻塞线程池线程)。
  • 最后,你执行SomeMethodC在 UI 线程上(最后一次阻塞线程池线程)。

如您所见,大部分时间 SomeMethodAsync正在执行(等待 SomeOtherMethodAsync 所花费的时间以及 Control.Invoke 调用之间的短暂时间除外)您仍在使用 UI 线程,但您也在阻塞您的线程池线程。因此,您现在占用了两个线程,大部分情况下只有其中一个线程在做有用的工作——另一个只是坐在那里等待。

除了阅读起来非常可怕之外,这是非常低效的。

考虑以下重写:

private async void control_SelectionValueChanged(Object sender, EventArgs e)
{
    try
    {
        await SomeMethodAsync();
    }
    catch (Exception ex)
    {
        // We're an async void, so don't forget to handle exceptions.
        MessageBox.Show(ex.Message);
    }
}

private async Task SomeMethodAsync()
{
    // We're on the UI thread, and we will stay on the UI
    // thread *at least* until we hit the `await` keyword.
    SomeMethodA();

    // We're still on the UI thread, but if `SomeOtherMethodAsync`
    // is a genuinely asynchronous method, we will go asynchronous
    // as soon as `SomeOtherMethodAsync` hits the its `await` on a
    // `Task` that does not transition to `Completed` state immediately.
    bool variable = await SomeOtherMethodAsync();

    // If you need stronger guarantees that `SomeOtherMethodAsync`
    // will stay off the UI thread, you can wrap it in Task.Run, so
    // that its synchronous portions (if any) run on a thread pool
    // thread (as opposed to the UI thread).
    // bool variable = await Task.Run(() => SomeOtherMethodAsync());

    // We're back on the UI thread for the remainder of this method.
    if ( variable ) SomeMethodB();

    // Still on the UI thread.
    SomeMethodC();
}

以上在行为方面是相似的(虽然不完全等同),但不是更容易阅读吗?

关于c# - 在一种方法中调用同步和异步方法(api/UI)的正确方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45280422/

相关文章:

c# - 如何从代码中设置 RSAProtectedConfigurationProvider 的 key 大小

c++ - 提升asio async_read : the read message adds to itself

python - 将同步重写为异步: not wait on a func

php - Laravel 异步上传文件

c# - 如何基于 Func<T> 将 IObservable<T> 窗口化/缓冲到 block 中

c# - 托管 .NET 等同于 WinBase 的 CreateFile 和 WriteFile (kernel32.dll)

javascript - 如何使用meteorhacks :npm package?调用这个wordcount函数

c# - 识别一系列异步操作的日志条目

c# - 我们如何在 StringBuilder 中添加字符串?

C# 命名和大小写约定