【问题标题】:I'm confused with an asynchronous request scenario (asp-net-core)我对异步请求方案(asp-net-core)感到困惑
【发布时间】:2020-04-12 15:36:59
【问题描述】:

我试图模拟一个场景,当我请求一次我的 api 服务时,我必须对不同的外部 API 或数据库等进行 4 次 IO 有界请求才能获得对我的示例的响应。

我想异步启动这些请求,如果我收到其中一个请求的否定响应(在我的示例中为 null),则在收到其中一个否定响应后立即停止所有请求。

根据我的示例,请求是同时启动的,即使我收到其中一个请求的否定响应,我也必须等到长请求(task4)完成。这意味着我至少能在 40 秒内得到结果。当我在第 20 秒收到来自第二个请求 (task2) 的空响应时,如何再不必要地停止工作 20 秒?

如果有可能我应该如何处理这种情况?

 public class HomeController : Controller
    {
       public async Task<string> SampleAsyncAction()
       {
          var task1 = DoSomeIOAsync(10000);
          var task2 = DoSomeIOAsync(20000);
          var task3 = DoSomeIOAsync(30000);
          var task4 = DoSomeIOAsync(40000);

          var result = await Task.WhenAll(task1, task2, task3, task4);

          if (result.Any(x=> x == null))
                return "Error occurred";
              return "Operation was done successfully in " + result.Sum().ToString() + "millisecond";
       }

       private int? DoSomeIO(int millisecond)
       {
          Thread.Sleep(millisecond);

          if (millisecond == 20000)
             return null;
          return (millisecond);
       }

       private async Task<int?> DoSomeIOAsync(int millisecond)
       {
          return await Task.Run(() => DoSomeIO(millisecond));
       }
    }

【问题讨论】:

  • 如果需要模拟异步调用,使用Task.Delay。不要使用 Thread.Sleep。然后你就可以消除 Task.Run 这通常是代码异味。
  • 如果您想在其中一个镜头返回某个响应时继续,那么您为什么要使用 Task.WhenAll?你读过它的定义吗?这与您想要的相反。
  • 看看这个:How can I await an array of tasks and stop waiting on first exception? 如果你可以让你的异步方法抛出异常而不是返回 null,你可以使用这个问题的解决方案。
  • 非常感谢@mason。我知道 Task.Delay 更好,但我想模拟使用未声明为异步的函数。如果我使用像 ORM 这样没有异步选择能力的东西怎么办?那么我不应该使用 Task.Run() 将我从 db 中选择的同步方法更改为异步方法吗?
  • @mason 我在异步编程方面并不完美。我认为 whenall() 是我可以并行执行一些 IO 请求的唯一方法。你对这种情况有什么建议?

标签: c# asp.net-core asynchronous async-await


【解决方案1】:

首先,只有异步工作可以被取消,并且只有当异步工作支持取消。像Thread.Sleep 这样的同步命令是不可取消的,所以你在这里尝试做的事情根本不可能开始。

Task.Run 并不是让同步突然变得异步的魔法棒。您所做的只是在新线程上运行同步代码。它仍然必须运行到完成,并且该线程将被绑定直到发生。

假设您实际上在处理异步代码,并且异步代码支持取消(通常,它是否接受 CancellationToken 参数),那么该怎么做您想要的是自定义CancellationTokenSource。您创建一个实例,并将其作为取消令牌传递给每个异步方法。然后等待每个操作,如果其中一个操作失败,则向 CTS 发送取消,这将导致使用该取消令牌的任何操作也取消。

【讨论】:

  • OP 可以通过取消等待操作来满足,而不是操作本身。可能不是一个好主意,但有可能。
  • 这将是让您的应用程序在 Web 服务器环境中崩溃的好方法。如果你正在做的工作是同步的/不可取消的,那么你最好只是等待它而不是随便产生线程。
  • @ChrisPratt 非常感谢您的回复。想象一下,您使用的 ORM 还没有为 select 准备异步方法。如何异步使用该同步方法?假设我上面写的函数(DoSomeIO)是ORM的同步选择函数,你能举个例子吗?
  • 你不能。如果 ORM 是同步的,那么它就是同步的。你不能只做异步的东西;它必须从根本上从上到下异步。
  • 也就是说,我不确定您使用的是哪种疯狂的反向 ORM,它不是异步的。数据库访问本质上是异步的,因此库仅公开一个同步 API 将是一个巨大的失败。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-28
  • 2013-01-20
  • 2011-07-23
  • 2020-10-23
  • 2021-07-26
相关资源
最近更新 更多