将同步代码包装为异步调用

埃德尔

我在ASP.NET应用程序中有一个方法,要花大量时间才能完成。在一个用户请求期间,此方法的调用最多可能发生3次,具体取决于用户提供的缓存状态和参数。每次通话大约需要1-2秒才能完成。该方法本身是对服务的同步调用,无法覆盖实现。
因此,对该服务的同步调用如下所示:

public OutputModel Calculate(InputModel input)
{
    // do some stuff
    return Service.LongRunningCall(input);
}

方法的用法是(请注意,方法的调用可能会发生多次):

private void MakeRequest()
{
    // a lot of other stuff: preparing requests, sending/processing other requests, etc.
    var myOutput = Calculate(myInput);
    // stuff again
}

我尝试从自己的角度更改实现,以提供此方法的同步工作,这就是到目前为止的内容。

public async Task<OutputModel> CalculateAsync(InputModel input)
{
    return await Task.Run(() =>
    {
        return Calculate(input);
    });
}

用法(“执行其他操作”代码的一部分与服务调用同时运行):

private async Task MakeRequest()
{
    // do some stuff
    var task = CalculateAsync(myInput);
    // do other stuff
    var myOutput = await task;
    // some more stuff
}

我的问题如下。我是否使用正确的方法来加快ASP.NET应用程序中的执行速度,还是在做不必要的工作以尝试异步运行同步代码?谁能解释为什么第二种方法不是ASP.NET中的一种选择(如果确实没有)?另外,如果这种方法适用,那么如果这是我们目前可能执行的唯一调用,我是否需要异步调用该方法(我有这种情况,在等待完成时没有其他事情要做)?
网络上有关该主题的大多数文章都介绍了将async-await方法与代码一起使用的方法,该awaitable方法已经提供了方法,但我的情况并非如此。这里是描述我的情况的不错的文章,它没有描述并行调用的情况,但拒绝了包装同步调用的选项,但是在我看来,我的情况正是这样做的时机。
在此先感谢您的帮助和提示。

斯蒂芬·克莱里

区分两种不同类型的并发很重要。异步并发是指在运行中有多个异步操作时(并且由于每个操作都是异步的,因此它们实际上都没有使用线程)。并行并发是指您有多个线程分别执行单独的操作。

首先要做的是重新评估这个假设:

该方法本身是对服务的同步调用,无法覆盖实现。

如果您的“服务”是Web服务或其他受I / O绑定的内容,那么最好的解决方案是为其编写异步API。

我将假设您的“服务”是一个CPU约束的操作,必须与Web服务器在同一台计算机上执行。

如果是这样,那么接下来要评估的是另一个假设:

我需要执行更快的请求。

您绝对确定这是您需要做的吗?您是否可以进行任何前端更改-例如,启动请求并允许用户在处理过程中做其他工作?

我将假设是,您确实确实需要使单个请求的执行速度更快。

在这种情况下,您需要在Web服务器上执行并行代码。通常,绝对不建议这样做,因为并行代码将使用ASP.NET可能需要处理其他请求的线程,并且通过删除/添加线程将使ASP.NET线程池试探法无效。因此,此决定确实会对您的整个服务器产生影响。

当您在ASP.NET上使用并行代码时,您将决定真正限制Web应用程序的可伸缩性。您还可能会看到相当多的线程搅动,尤其是在您的请求非常突发的情况下。我建议仅在ASP.NET上使用并行代码,如果您知道并发用户数会非常少(即,不是公共服务器)。

因此,如果您走到了这一步,并且确定要在ASP.NET上进行并行处理,那么您有两种选择。

一种更简单的方法是使用Task.Run,与您现有的代码非常相似。但是,我不建议实现一种CalculateAsync方法,因为这意味着处理是异步的(不是)。而是Task.Run在调用时使用:

private async Task MakeRequest()
{
  // do some stuff
  var task = Task.Run(() => Calculate(myInput));
  // do other stuff
  var myOutput = await task;
  // some more stuff
}

另外,如果你的代码工作得很好,你可以使用Parallel类型,即Parallel.ForParallel.ForEachParallel.InvokeParallel代码的优点在于,请求线程被用作并行线程之一,然后在线程上下文中继续执行(上下文切换比async示例少):

private void MakeRequest()
{
  Parallel.Invoke(() => Calculate(myInput1),
      () => Calculate(myInput2),
      () => Calculate(myInput3));
}

我根本不建议在ASP.NET上使用并行LINQ(PLINQ)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章