将同步代码包装到asynchronous调用中

我在ASP.NET应用程序中有一个方法,需要花费很多时间才能完成。 根据用户提供的caching状态和参数,在一次用户请求期间,对此方法的调用最多可能发生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应用程序的执行或我做不必要的工作,尝试asynchronous运行同步代码? 任何人都可以解释为什么第二种方法不是在ASP.NET中的选项(如果它真的不是)? 另外,如果这种方法适用,我是否需要asynchronous调用这种方法,如果它是我们现在可能执行的唯一调用(我有这种情况,那么在等待完成时没有其他的东西需要做)?
在这个主题的networking中的大多数文章涵盖了使用async-await方法的代码,已经提供了awaitable方法,但这不是我的情况。 这里是描述我的情况的好文章,它没有描述并行调用的情况,拒绝了同步调用的选项,但在我看来,我的情况正是这样做的机会。
在此先感谢您的帮助和提示。

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

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

该方法本身是对服务的同步调用,并且不可能覆盖实现。

如果您的“服务”是一个Web服务或其他任何I / O绑定,那么最好的解决scheme是为它编写一个asynchronousAPI。

我将继续假设您的“服务”是必须在Web服务器所在的机器上执行的CPU绑定操作。

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

我需要更快的执行请求。

你确定这是你需要做的? 是否有任何前端更改可以替代 – 例如,启动请求并允许用户在处理过程中执行其他工作?

我将继续假设是的,你确实需要使个别请求更快地执行。

在这种情况下,您需要在Web服务器上执行并行代码。 这是绝对不推荐的一般,因为并行代码将使用ASP.NET可能需要处理其他请求的线程,并通过删除/添加线程将抛出ASP.NET线程池启发式。 所以,这个决定对整个服务器都有影响。

当您在ASP.NET上使用并行代码时,您决定真正限制您的Web应用程序的可伸缩性。 你也可能会看到相当数量的线程stream失,特别是如果你的请求是突发性的。 如果您知道同时在线的用户数量非常less(即不是公用服务器),我build议只在ASP.NET上使用并行代码。

所以,如果你得到这么多,你确定你想要在ASP.NET上进行并行处理,那么你有两个select。

更简单的方法之一是使用Task.Run ,非常类似于您现有的代码。 但是,我不build议实现CalculateAsync方法,因为这意味着处理是asynchronous的(不是这样)。 相反,在调用点使用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 } 

或者,如果它与您的代码一起工作良好,则可以使用Paralleltypes,即Parallel.ForParallel.ForEachParallel.InvokeParallel代码的优点在于,请求线程被用作并行线程之一,然后在线程上下文中恢复执行(与async示例相比,上下文切换更less):

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

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